在线支付功能的设计及其实现

们都知道,在现在的网站中,基本上都会有支付功能,在线支付作为一个潮流已是现代化网站的必备功能模块,那么几天我就分享一下如果来做这个在线支付功能。

在线支付一般来说有两种实现方式,一种是调用各个银行提供的接口,另一种是使用第三方集成好的支付功能。两种方式各有优劣,这个相信行内人士都是非常了解的了。对于第三方支付来说会需要提交企业5证来验证,还会有部分手续费,但是实现起来就非常方便了。对于直接使用银联接口的话就是使用起来必来麻烦,要为各个银行写接口实现,但是相比起来就更加安全了。

 

本文主要讲的是使用第三方支付平台来整合到我们的项目中,实现实际的支付功能。本文使用的例程是一个真实的支付过程,请注意,不要再测试中支付过多金额,否则后果自负。以前文说到的一个网上书店系统来说明,这里接入的是易宝支付的测试指纹和接口。如实际企业开发者可以使用企业牌照申请接口,如是普通开发者用来测试在线支付功能的可以直接使用。

 

我们先来说应该如何生成订单。

一、数据库设计

我们需要来一个订单表,订单详情表,以及订单的自动化序列表

 

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

--订单表

create table orders(

    ordernum varchar(100) primary key,

    price float(8,2),

    number int,

    status int,   --支付成功状态位会改变

    customerId VARCHAR(100),

    CONSTRAINT customerId_fk FOREIGN KEY (customerId) REFERENCES customers(id) 

)

)

 

--订单详情表

create table orderitems(

    id varchar(100) primary key,

    number int,

    price float(8,2),

    ordernum varchar(100),

    bookid varchar(100),

    CONSTRAINT ordernum_fk FOREIGN KEY (ordernum) REFERENCES orders(ordernum),

    CONSTRAINT bookid_fk FOREIGN KEY (bookid) REFERENCES books(id)   

)

 

--订单编号表

create table ordernum(

    prefix date,

    num int

)

 

二、bean设计

 

生成其get,set方法,并且记得要序列化Serializable

 

?

1

2

3

4

private String ordernum;

    private float price;

    private int number;

    private int status;


 

 

三、接口设计

设计订单的实体类

 

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

public interface OrderDao {

 

    void save(Order order);

 

    Order findByNum(String ordernum);

 

    void update(Order order);

 

    //订单号降序排序

    List<order> findByCustomerId(String customerId);

 

     

    List<orderitem> findOrderItem(String ordernum);

 

}</orderitem></order>


接口的实现

 

 

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

public class OrderDaoImpl implements OrderDao {

 

    private QueryRunner qr=new QueryRunner(C3P0Util.getDataSource());

     

    //保存订单

    @Override

    public void save(Order order) {

         

        try {

            qr.update("insert into orders (ordernum,price,number,status,customerId) values (?,?,?,?,?)",

                    order.getOrdernum(),order.getPrice(),order.getNumber(),order.getStatus(),

                    order.getCustomer()==null?null:order.getCustomer().getId());

            List<orderitem> items = order.getItems();

            for(OrderItem item:items){

                qr.update("insert into orderitems (id,number,price,ordernum,bookid) values (?,?,?,?,?)",

                        item.getId(),item.getNumber(),item.getPrice(),order.getOrdernum(),item.getBook()==null?null:item.getBook().getId());

            }

        } catch (SQLException e) {

            throw new RuntimeException(e);

        }

    }

 

 

    public Order findByNum(String ordernum) {

        try {

            Order order = qr.query("select * from orders where ordernum=?", new BeanHandler<order>(Order.class), ordernum);

            if(order!=null){

                Customer customer = qr.query("select * from customers where id=(select customerId from orders where ordernum=?)", new BeanHandler<customer>(Customer.class), ordernum);

                order.setCustomer(customer);

            }

            return order;

        } catch (SQLException e) {

            throw new RuntimeException(e);

        }

    }

    public void update(Order order) {

        try {

            qr.update("update orders set price=?,number=?,status=? where ordernum=?", order.getPrice(),order.getNumber(),order.getStatus(),order.getOrdernum());

        } catch (SQLException e) {

            throw new RuntimeException(e);

        }

    }

 

 

    @Override

    public List<order> findByCustomerId(String customerId) {

        try {

            List<order> orders=qr.query("select * from orders where customerId=?  order by ordernum desc ", new BeanListHandler<order>(Order.class),customerId);

            if(orders!=null){

                Customer customer=qr.query("select * from customers where id=? ",new BeanHandler<customer>(Customer.class),customerId);

                for (Order order : orders) {

                    order.setCustomer(customer);

                }

            }

            return orders;

        } catch (SQLException e) {

            throw new RuntimeException(e);

        }

    }

 

 

    @Override

    public List<orderitem> findOrderItem(String ordernum) {

         

        try {

            List<orderitem> items = qr.query("select * from orderitems where ordernum=?", new BeanListHandler<orderitem>(OrderItem.class), ordernum);

            if(items!=null){

                for(OrderItem o:items){

                    Book book = qr.query("select * from books where id=(select bookId from orderitems where id=?)", new BeanHandler<book>(Book.class), o.getId());

                    o.setBook(book);

                }

            }

            return items;

        } catch (SQLException e) {

            throw new RuntimeException(e);

        }

    }

}

</book></orderitem></orderitem></orderitem></customer></order></order></order></customer></order></orderitem>

 

四、Service的设计

 

 

 

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

//生成订单

    void genOrder(Order order);

 

    //根据订单号查找订单

    Order findOrderByNum(String ordernum);

 

    //更新订单信息

    void updateOrder(Order order);

     

    //更新订单状态

    void changeOrderStatus(int status,String ordernum);

 

    //

    List<order> findOrdersByCustomerId(String customerId);

     

    List<orderitem> findOrderItemByCustomerId(String ordernum);

</orderitem></order>


 

 

实现其接口

 

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

//生成订单

    @Override

    public void genOrder(Order order) {

        if(order==null)

                throw new RuntimeException("订单不能为空");

        if(order.getCustomer()==null)

            throw new RuntimeException("订单的客户不能为空");

        orderDao.save(order);

         

    }

 

    @Override

    public Order findOrderByNum(String ordernum) {

         

        return orderDao.findByNum(ordernum);

    }

 

    @Override

    public void updateOrder(Order order) {

        orderDao.update(order);

         

    }

 

    @Override

    public void changeOrderStatus(int status, String ordernum) {

        Order order=findOrderByNum(ordernum);

        order.setStatus(status);

        updateOrder(order);

    }

 

    @Override

    public List<order> findOrdersByCustomerId(String customerId) {

         

        return orderDao.findByCustomerId(customerId);

    }

 

    @Override

    public List<orderitem> findOrderItemByCustomerId(String ordernum) {

     

        return orderDao.findOrderItem(ordernum);

    }</orderitem></order>

 

 

 

生成订单

 

 

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

//订单详情

    private void showOrders(HttpServletRequest request,

            HttpServletResponse response) throws IOException, ServletException {

        //检测是否登录;

                HttpSession session=request.getSession();

                Customer customer=(Customer) session.getAttribute("customer");

                if(customer==null){

                    response.getWriter().write("请先登录");

                    response.setHeader("Refresh", "2;URL="+request.getContextPath());

                    return ;

                }

            List<order>  orders=s.findOrdersByCustomerId(customer.getId());

            request.setAttribute("orders", orders);

             

            request.getRequestDispatcher("/listOrders.jsp").forward(request, response);

                 

                 

         

    }

 

 

    //生成订单

    private void genOrder(HttpServletRequest request,

            HttpServletResponse response) throws IOException, ServletException  {

        //检测是否登录;

        HttpSession session=request.getSession();

        Customer customer=(Customer) session.getAttribute("customer");

        if(customer==null){

            response.getWriter().write("请先登录");

            response.setHeader("Refresh", "2;URL="+request.getContextPath());

            return ;

        }

         

        Cart cart=(Cart) request.getSession().getAttribute("cart");

         

        Order order=new Order();

        order.setOrdernum(OrderNumUtil.genOrderNum());

        order.setPrice(cart.getPrice());

        order.setNumber(cart.getNumber());

        order.setCustomer(customer);

         

         

        List<orderitem>  oItems=new ArrayList<orderitem>();

        //设置订单项

        for(Map.Entry<string, cartitem="">  me:cart.getItems().entrySet()){

            OrderItem item=new OrderItem();

            item.setId(UUID.randomUUID().toString());

            item.setNumber(me.getValue().getNumber());

            item.setPrice(me.getValue().getPrice());

            item.setBook(me.getValue().getBook());

            oItems.add(item);

        }

        //建立和订单的关系

        order.setItems(oItems);

        s.genOrder(order);

        request.setAttribute("order", order);

        request.getRequestDispatcher("/pay.jsp").forward(request, response);

         

         

    }

</string,></orderitem></orderitem></order>


 

 


 

接下来就是支付功能的实现了。我们要为上面生成的订单来支付。

五、界面设计。

订单生成后腰跳转过去支付,这个支付界面我们可以使用一个表单。pay.jsp。并且将内容提交到PayServlet中。

 

\

 

?

1

 

订单号: 支付金额:元
 
请您选择在线支付银行
招商银行工商银行农业银行建设银行
中国民生银行总行光大银行交通银行深圳发展银行
北京银行兴业银行上海浦东发展银行中信银行
 
 


 

 

六、servletc逻辑处理

对于使用易宝支付我们无需导入jar包,我们直接使用其接口即可。下面这张图片是易宝支付的接口说明。

 

\

 

PayServlet的内容。

这里值得说一下的就是 p8_Url ,也就是支付成功后悔返回的商家界面地址。我这里写的是自己工程的地址。

 

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

public void doGet(HttpServletRequest request, HttpServletResponse response)

            throws ServletException, IOException {

 

        request.setCharacterEncoding("UTF-8");

        String ordernum=request.getParameter("ordernum");

        String money=request.getParameter("money");

        String pd_FrpId=request.getParameter("pd_FrpId");

         

         

         

        String p0_Cmd = "Buy";

        String p1_MerId = "10001126856";

        String p2_Order = ordernum;

        String p3_Amt = money;

        String p4_Cur = "CNY";

        String p5_Pid = "books" //商品名称

        String p6_Pcat = "unknown";

        String p7_Pdesc = "descrition";

        String p8_Url = "http://localhost:8080"+request.getContextPath()+"/servlet/ResponsePayServlet";

        String p9_SAF = "1";

        String pa_MP = "unknown";

        String pr_NeedResponse="1";

        String hmac = PaymentUtil.buildHmac(p0_Cmd, p1_MerId, p2_Order, p3_Amt, p4_Cur, p5_Pid, p6_Pcat, p7_Pdesc, p8_Url, p9_SAF, pa_MP, pd_FrpId, pr_NeedResponse, "69cl522AV6q613Ii4W6u8K6XuW8vM1N6bFgyv769220IuYe9u37N4y7rI4Pl");

         

        request.setAttribute("p0_Cmd",p0_Cmd );

        request.setAttribute("p1_MerId",p1_MerId );

        request.setAttribute("p2_Order", p2_Order);

        request.setAttribute("p3_Amt", p3_Amt);

        request.setAttribute("p4_Cur",p4_Cur );

        request.setAttribute("p5_Pid",p5_Pid );

        request.setAttribute("p6_Pcat",p6_Pcat );

        request.setAttribute("p7_Pdesc",p7_Pdesc );

        request.setAttribute("p8_Url",p8_Url );

        request.setAttribute("pa_MP",pa_MP );

        request.setAttribute("pr_NeedResponse",pr_NeedResponse );

        request.setAttribute("hmac",hmac );

        request.setAttribute("p9_SAF",p9_SAF );

        request.setAttribute("pd_FrpId", pd_FrpId);

         

        request.getRequestDispatcher("/sure.jsp").forward(request, response);

     

    }


PayUtil.java是一个工具类,这里我们直接使用官方的即可。

 

 

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

public class PaymentUtil {

 

private static String encodingCharset = "UTF-8";

     

    /**

     * 生成hmac方法

     *

     * @param p0_Cmd 业务类型

     * @param p1_MerId 商户编号

     * @param p2_Order 商户订单号

     * @param p3_Amt 支付金额

     * @param p4_Cur 交易币种

     * @param p5_Pid 商品名称

     * @param p6_Pcat 商品种类

     * @param p7_Pdesc 商品描述

     * @param p8_Url 商户接收支付成功数据的地址

     * @param p9_SAF 送货地址

     * @param pa_MP 商户扩展信息

     * @param pd_FrpId 银行编码

     * @param pr_NeedResponse 应答机制

     * @param keyValue 商户密钥

     * @return

     */

    public static String buildHmac(String p0_Cmd,String p1_MerId,

            String p2_Order, String p3_Amt, String p4_Cur,String p5_Pid, String p6_Pcat,

            String p7_Pdesc,String p8_Url, String p9_SAF,String pa_MP,String pd_FrpId,

            String pr_NeedResponse,String keyValue) {

        StringBuilder sValue = new StringBuilder();

        // 业务类型

        sValue.append(p0_Cmd);

        // 商户编号

        sValue.append(p1_MerId);

        // 商户订单号

        sValue.append(p2_Order);

        // 支付金额

        sValue.append(p3_Amt);

        // 交易币种

        sValue.append(p4_Cur);

        // 商品名称

        sValue.append(p5_Pid);

        // 商品种类

        sValue.append(p6_Pcat);

        // 商品描述

        sValue.append(p7_Pdesc);

        // 商户接收支付成功数据的地址

        sValue.append(p8_Url);

        // 送货地址

        sValue.append(p9_SAF);

        // 商户扩展信息

        sValue.append(pa_MP);

        // 银行编码

        sValue.append(pd_FrpId);

        // 应答机制

        sValue.append(pr_NeedResponse);

         

        return PaymentUtil.hmacSign(sValue.toString(), keyValue);

    }

     

    /**

     * 返回校验hmac方法

     *

     * @param hmac 支付网关发来的加密验证码

     * @param p1_MerId 商户编号

     * @param r0_Cmd 业务类型

     * @param r1_Code 支付结果

     * @param r2_TrxId 易宝支付交易流水号

     * @param r3_Amt 支付金额

     * @param r4_Cur 交易币种

     * @param r5_Pid 商品名称

     * @param r6_Order 商户订单号

     * @param r7_Uid 易宝支付会员ID

     * @param r8_MP 商户扩展信息

     * @param r9_BType 交易结果返回类型

     * @param keyValue 密钥

     * @return

     */

    public static boolean verifyCallback(String hmac, String p1_MerId,

            String r0_Cmd, String r1_Code, String r2_TrxId, String r3_Amt,

            String r4_Cur, String r5_Pid, String r6_Order, String r7_Uid,

            String r8_MP, String r9_BType, String keyValue) {

        StringBuilder sValue = new StringBuilder();

        // 商户编号

        sValue.append(p1_MerId);

        // 业务类型

        sValue.append(r0_Cmd);

        // 支付结果

        sValue.append(r1_Code);

        // 易宝支付交易流水号

        sValue.append(r2_TrxId);

        // 支付金额

        sValue.append(r3_Amt);

        // 交易币种

        sValue.append(r4_Cur);

        // 商品名称

        sValue.append(r5_Pid);

        // 商户订单号

        sValue.append(r6_Order);

        // 易宝支付会员ID

        sValue.append(r7_Uid);

        // 商户扩展信息

        sValue.append(r8_MP);

        // 交易结果返回类型

        sValue.append(r9_BType);

        String sNewString = PaymentUtil.hmacSign(sValue.toString(), keyValue);

        return sNewString.equals(hmac);

    }

     

    /**

     * @param aValue

     * @param aKey

     * @return

     */

    public static String hmacSign(String aValue, String aKey) {

        byte k_ipad[] = new byte[64];

        byte k_opad[] = new byte[64];

        byte keyb[];

        byte value[];

        try {

            keyb = aKey.getBytes(encodingCharset);

            value = aValue.getBytes(encodingCharset);

        } catch (UnsupportedEncodingException e) {

            keyb = aKey.getBytes();

            value = aValue.getBytes();

        }

 

        Arrays.fill(k_ipad, keyb.length, 64, (byte) 54);

        Arrays.fill(k_opad, keyb.length, 64, (byte) 92);

        for (int i = 0; i < keyb.length; i++) {

            k_ipad[i] = (byte) (keyb[i] ^ 0x36);

            k_opad[i] = (byte) (keyb[i] ^ 0x5c);

        }

 

        MessageDigest md = null;

        try {

            md = MessageDigest.getInstance("MD5");

        } catch (NoSuchAlgorithmException e) {

 

            return null;

        }

        md.update(k_ipad);

        md.update(value);

        byte dg[] = md.digest();

        md.reset();

        md.update(k_opad);

        md.update(dg, 0, 16);

        dg = md.digest();

        return toHex(dg);

    }

 

    public static String toHex(byte input[]) {

        if (input == null)

            return null;

        StringBuffer output = new StringBuffer(input.length * 2);

        for (int i = 0; i < input.length; i++) {

            int current = input[i] & 0xff;

            if (current < 16)

                output.append("0");

            output.append(Integer.toString(current, 16));

        }

 

        return output.toString();

    }

 

    /**

     *

     * @param args

     * @param key

     * @return

     */

    public static String getHmac(String[] args, String key) {

        if (args == null || args.length == 0) {

            return (null);

        }

        StringBuffer str = new StringBuffer();

        for (int i = 0; i < args.length; i++) {

            str.append(args[i]);

        }

        return (hmacSign(str.toString(), key));

    }

 

    /**

     * @param aValue

     * @return

     */

    public static String digest(String aValue) {

        aValue = aValue.trim();

        byte value[];

        try {

            value = aValue.getBytes(encodingCharset);

        } catch (UnsupportedEncodingException e) {

            value = aValue.getBytes();

        }

        MessageDigest md = null;

        try {

            md = MessageDigest.getInstance("SHA");

        } catch (NoSuchAlgorithmException e) {

            e.printStackTrace();

            return null;

        }

        return toHex(md.digest(value));

 

    }

 

     

}


 

 

对于支付成功之后,我们需要修改我们的订单状态,改为已付款,所以我们需要一个响应的servlet

 

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

public class ResponsePayServlet extends HttpServlet {

 

 

    private BusinessService s=new BusinessServiceImpl();

     

    public void doGet(HttpServletRequest request, HttpServletResponse response)

            throws ServletException, IOException {

        response.setContentType("text/html;charset=UTF-8");

        PrintWriter out = response.getWriter();

        String p1_MerId = request.getParameter("p1_MerId");

        String r0_Cmd = request.getParameter("r0_Cmd");

        String r1_Code = request.getParameter("r1_Code");//支付结果。1代表成功

        String r2_TrxId = request.getParameter("r2_TrxId");

        String r3_Amt = request.getParameter("r3_Amt");

        String r4_Cur= request.getParameter("r4_Cur");

        String r5_Pid= request.getParameter("r5_Pid");

        String r6_Order = request.getParameter("r6_Order");//订单编号

        String r7_Uid = request.getParameter("r7_Uid");

        String r8_MP = request.getParameter("r8_MP");

        String r9_BType = request.getParameter("r9_BType");//1浏览器访问的。2点对点

        String hmac = request.getParameter("hmac");

         

        //数据校验

        boolean ok = PaymentUtil.verifyCallback(hmac, p1_MerId, r0_Cmd, r1_Code, r2_TrxId, r3_Amt, r4_Cur, r5_Pid, r6_Order, r7_Uid, r8_MP, r9_BType, "69cl522AV6q613Ii4W6u8K6XuW8vM1N6bFgyv769220IuYe9u37N4y7rI4Pl");

        if(!ok){

            out.write("数据有可能被篡改,请联系网站");

        }else{

            if("1".equals(r1_Code)){

                //支付成功:根据订单号更改订单状态。  点卡或充值时注意表单的重复提交问题。

                if("2".equals(r9_BType)){

                    out.write("success");

                }

                 

                //更改订单的状态

                /*Order order=s.findOrderByNum(r6_Order);

                order.setStatus(1);  //1表示已付款,0表示未付款

                s.updateOrder(order);*/

                s.changeOrderStatus(1, r6_Order);

                request.getSession().removeAttribute("cart");

                response.setHeader("Refresh", "2;URL="+request.getContextPath());

            }

        }

    }

 

 

    public void doPost(HttpServletRequest request, HttpServletResponse response)

            throws ServletException, IOException {

         

        doGet(request,response);

         

     

    }

 

}


 

 

在这个网上书店的支付中,我们可以看到这样的效果:这里以支付1分钱为例,来演示在线支付功能。

用户在浏览商品后加入购物车并生成订单了。

 

\

先是跳转我们自己写的这个选择银行的表单中。

\

支付的过程我们会先跳转到易宝支付的页面。

 

\

我这里选择建设银行来支付。所以点击确定后就会跳转到建设银行的界面。

 

\

 

 

支付成功之后就返回。

\

 

 

然后就会跳转回我们自己的项目中,这个时候来查看一个订单的状态,会发现已经支付成功了。

\

 

 

总结:在线支付功能是个非常实用的功能,我们都应该需要掌握其基本的开发流程,并亲自动手进行测试才会更加清楚了了解。

  • 2
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值