尚品汇总结八:支付模块(面试专用)

一、支付宝介绍

1 支付宝简介

      支付宝(中国)网络技术有限公司 [1]  是国内的第三方支付平台,致力于提供简单、安全、快速的支付解决方案 [2]  。支付宝公司从2004年建立开始,始终以信任作为产品和服务的核心。旗下有支付宝支付宝钱包两个独立品牌。自2014年第二季度开始成为当前全球最大的移动支付厂商。

       当用户提交订单会跳转到选择支付渠道页面!

      当用户点击立即支付时生成支付的二维码

使用支付宝app 进行扫码支付

2 过程分析

3 对接支付宝的准备工作

1、申请条件

1.  企业或个体工商户可申请;

2.  提供真实有效的营业执照,且支付宝账户名称需与营业执照主体一致;

3.  网站能正常访问且页面信息有完整商品内容;

4.  网站必须通过ICP备案,个体户备案需与账户主体一致。

(团购类网站不支持个体工商户签约)

支付手续费

4 申请步骤:

  1. 支付宝商家中心中申请  Alipay
  2. https://b.alipay.com/signing/productSetV2.htm?mrchportalwebServer=https%3A%2F%2Fmrchportalweb.alipay.com

一个工作日后登录到蚂蚁金服开发者中心中:

可以查看到一个已经签约上线的应用。 其中非常重要的是这个APPID,需要记录下来之后的程序中要用到这个参数。

点击查看

到此为止,电商网站可以访问支付宝的最基本的准备已经完成。

支付宝开发手册:小程序文档 - 支付宝文档中心

二、支付功能实现

1  思路分析

  1. 将支付数据保存到数据库,以便跟支付宝进行对账
  2. 生成要支付的二维码

生成二维码需要的参数列表请参考官方文档

https://docs.open.alipay.com/api_1/alipay.trade.page.pay/

2  保存支付信息 – 制作实体类

表结构 payment_info

id

主键自动生成

out_trade_no

订单中已生成的对外交易编号。订单中获取

order_id

订单编号

payment_type

支付类型(微信与支付宝)

trade_no

交易号,回调时生成

total_amount

订单金额。订单中获取

subject

交易内容。利用商品名称拼接。

payment_status

支付状态,默认值未支付。

create_time

创建时间,当前时间。

callback_time

回调时间,初始为空,支付宝异步回调时记录

callback_content

回调信息,初始为空,支付宝异步回调时记录

PaymentStatus

public enum PaymentStatus {

    UNPAID("未支付"),

()支付中
    PAID("已支付"),
    PAY_FAIL("支付失败"),
    ClOSED("已关闭");

    private String name ;

    PaymentStatus(String name) {
        this.name=name;
    }
}

3  编写支付宝支付接口

4.1  制作AlipayClient工具类

在resource中添加alipay.properties

alipay_url=https://openapi.alipay.com/gateway.do

app_id=2018020102122556

app_private_key=MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCdQeknhM2rhiGAH6V0ljxn3rAWIdzduTEQuteTfwjnZtvMhQPuuN1b/88D5yMuaZhZNFeUdWb+SmtP9DAzAWWgnT13T0YhJcxP6txm7JBRrjadCRt+LOFxPiPQk5t9fH7yXjw9i4uMDsNJeTncrVZ/AZYrk0ESC9anJR8XeuBc3HE8T4fqlKKl35jlumIWrPbPNQhKGXaGcOnpiaXO9qYYUSP/tnrjNYXHOso0yBs4YTl+LLX2TJ12p3n/oX6HnL4zQgtN5k4QasHP7CIig1ngcVQGfWsMm4djI9KXNXvGLQPfMQEmyb71mM5OCdl1MtAc6OaIAymhSv2hOLNIuyodAgMBAAECggEAe05/P5mGm4QlKI2n8u8KlneqovASe1kG/BNFjkYB+VBR8OAr4TfbepPvAyRuFap+5xN/yMz14VcBJkRWtufVhEdHNxJV7w/wUIncIGhGEYYFFMVbZWhTrbQH6TiUp6TC9dCmc6vD1CKPRkFj+YGBXT0lPy3LzBa0TYNyCbszyhthrgkpuFYbB0R93IPvvBh5NJFXQytwNb2oVopC9AQWviqnZUZcT0eJ087dQ1WLPa6blBD8DP1PUq0Ldr6pgKfObFxIj8+87DlJznRfdEsbqZlS7jagdw5tLr71WJpctIGPqKpgvajfePP/lj3eY82BKQB+aTw0zmAiB05Yes4LgQKBgQDq3EiQR8J1MEN2rpiLt1WvDYYvKVUgOY7Od//fRPgaMBstbe4TzGBpR8E+z267bHAWLaWtHkfX6muFHn1x68ozEUWk/nZq0smWnuPdcy4E7Itbk36W2FF/rOZB7j5ddlC9byrxDSNgcf9/FA/CU+i5KVQpLYfsk2dvwomvu0aFVQKBgQCraXpxzMmsBx4127LsZDO5bxfxb6nqzyK4NPe0VaGiRg8oaCWczcLz1J5iRqC9QeEwsSt4XU1sYBMTcsFpA0apZpm3prH2HJRx/isNENesaHcihF0mMd0WxU3xyRvWSDeZV5A1Zy1ZEJ+p17DGwb2j+yo2uBrDNXBgBWEzXwiRqQKBgBdXFvsHtqKQzlOQHGbeLGy+KlSrheMy9Sc9s7cLkqB/oWPNZfifugEceW71jGqh5y29EZb3yGoDyPWsxwi4Rxr2H3a7Nyd8lT4bwkdyt+MTYvIR4WW6T7chhqyMsbP2GyYIUzsrdBWUnrCRXNOSJTGpksyY0sZHC+OGcMp/EQ4VAoGBAIISSVL/pm1+/UK7U1ukcced8JpKNLM0uVD1CJ50eHHOHgR4e0owrWYfioxisejLjBlJ6AWvL2g0w2T3qKKKVN2JOM4ulU5/w3l4+KwygqaWowizTogEQJPd5ta52ADTzjTzSD/t6nByd+YHAWLhc4lyt0bMj6pf68VBb8/upm75AoGAGAYz79IVHp9eppykufjNcWu6okkG8tZnzuyaWKW/CuKKBWMaTk0vcyQlfJfxIBccoQrBuYyXBdcpPuZ/ys2C25pNrkACuhIKNgnMc0floJoYEfJzetw/3cIimWu4NJzVQOaojaGA58oo2+fub43Xn25Jq4rvSVe3oLdb5xWkw5Q=

alipay_public_key=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhkZi6W0wn/prX+NIIF9ATb5Z8ReKK4hFYtBrweDfGHD1mNW7YIZY4G5hE7S2Sry8eFXlFgSlBWlJ4fVnDaK9MkVThpwE2H65ooVlK/wLuyPqovIVpMt/utva5Ayuzv7eQOWK45FdLDNDlK8QLoBko6SS+YbnWnf7a+mrf4NAS4UFClpfe8Byqe8XIraO2Cg4Ko5Y5schX39rOAH8GlLdgqQRYVQ2dCnkIQ+L+I4Cy9Mvw3rIkTwt3MBU+AqREXY4r5Bn6cmmX/9MAJbFqrofGiUAqG+qbjTcZAzgNPfuiD0zXgt/YYjMQMzck75BOmwnYOam2ajODUSQn8Xybsa7wQIDAQAB

return_payment_url=http://api.gmall.com/api/payment/alipay/callback/return

notify_payment_url=http://xsiv7k.natappfree.cc/api/payment/alipay/callback/notify

return_order_url=http://payment.gmall.com/pay/success.html

 
 
 
创建配置类
package com.atguigu.gmall.payment.config;

  

  

  @Configuration

@PropertySource("classpath:alipay.properties")

  public class AlipayConfig {

  

    @Value("${alipay_url}")

    private String alipay_url;

  

    @Value("${app_private_key}")

    private String app_private_key;

  

    @Value("${app_id}")

    private String app_id;

  

  

    public final static String format="json";

    public final static String charset="utf-8";

    public final static String sign_type="RSA2";

    public static String return_payment_url;

    public static  String notify_payment_url;

    public static  String return_order_url;

    public static  String alipay_public_key;

  

    @Value("${alipay_public_key}")

    public   void setAlipay_public_key(String alipay_public_key) {

        AlipayConfig.alipay_public_key = alipay_public_key;

    }

    @Value("${return_payment_url}")

    public   void setReturn_url(String return_payment_url) {

        AlipayConfig.return_payment_url = return_payment_url;

    }

    @Value("${notify_payment_url}")

    public   void setNotify_url(String notify_payment_url) {

        AlipayConfig.notify_payment_url = notify_payment_url;

    }

  

    @Value("${return_order_url}")

    public   void setReturn_order_url(String return_order_url) {

        AlipayConfig.return_order_url = return_order_url;

    }

    @Bean

    public AlipayClient alipayClient(){

        // AlipayClient alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do", APP_ID, APP_PRIVATE_KEY, FORMAT, CHARSET, ALIPAY_PUBLIC_KEY, SIGN_TYPE); //获得初始化的AlipayClient

        AlipayClient alipayClient=new DefaultAlipayClient(alipay_url,app_id,app_private_key,format,charset, alipay_public_key,sign_type );

        return alipayClient;

    }

}

4.2 编写支付宝下单接口

实现类

package com.atguigu.gmall.payment.service.impl;

  @Service

  public class AlipayServiceImpl implements AlipayService {

  

    @Autowired

    private PaymentService paymentService;

  

    @Autowired

    private OrderFeignClient orderFeignClient;

  

    @Autowired

    private AlipayClient alipayClient;

  

    @Override

    public String createaliPay(Long orderId) throws AlipayApiException {

        OrderInfo orderInfo  = orderFeignClient.getOrderInfo(orderId);

        // 保存交易记录

        paymentService.savePaymentInfo(orderInfo, PaymentType.ALIPAY.name());

        // 生产二维码

        AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();//创建API对应的request

        // 同步回调

             alipayRequest.setReturnUrl(AlipayConfig.return_payment_url);

        // 异步回调

        alipayRequest.setNotifyUrl(AlipayConfig.notify_payment_url);//在公共参数中设置回跳和通知地址

        // 参数

        // 声明一个map 集合

        HashMap<String, Object> map = new HashMap<>();

        map.put("out_trade_no",orderInfo.getOutTradeNo());

        map.put("product_code","FAST_INSTANT_TRADE_PAY");

        map.put("total_amount",orderInfo.getTotalAmount());

        map.put("subject","test");

        alipayRequest.setBizContent(JSON.toJSONString(map));

        return alipayClient.pageExecute(alipayRequest).getBody(); //调用SDK生成表单;

    }
}

 
AlipayController 
@Controller
@RequestMapping("/api/payment/alipay")
public class AlipayController {

    @Autowired
    private AlipayService alipayService;

     @RequestMapping("submit/{orderId}")

@ResponseBody
    public String submitOrder(@PathVariable(value = "orderId") Long orderId, HttpServletResponse response){
        String from = "";
        try {
            from = alipayService.createaliPay(orderId);
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }
        return from;
    }
}

三、 支付后回调—同步回调

AlipayController

/**
 * 支付宝回调
 * @return
 */
@RequestMapping("callback/return")
public String callBack() {
    // 同步回调给用户展示信息
    return "redirect:" + AlipayConfig.return_order_url;
}

这里requestMapping对应的路径必须与之前传给支付宝的AlipayConfig.return_order_url保持一致。

四、 支付宝回调—异步回调

异步回调有两个重要的职责:

确认并记录用户已付款通知电商模块。新版本的支付接口已经取消了同步回调的支付结果传递。所以用户付款成功与否全看异步回调。

接收到回调要做的事情:

  1. 验证回调信息的真伪
  2. 验证用户付款的成功与否
  3. 把新的支付状态写入支付信息表{paymentInfo}中。
  4. 通知电商其他模块
  5. 给支付宝返回回执。

AlipayController

/**

     * 支付宝异步回调  必须使用内网穿透

     * @param paramMap

     * @param request

     * @return

     */

    @RequestMapping("callback/notify")
@ResponseBody

    public String alipayNotify(@RequestParam Map<String, String> paramMap) {

        System.out.println("回来了!");

        boolean signVerified = false; //调用SDK验证签名

        try {

            signVerified = AlipaySignature.rsaCheckV1(paramMap, AlipayConfig.alipay_public_key, AlipayConfig.charset, AlipayConfig.sign_type);

        } catch (AlipayApiException e) {

            e.printStackTrace();

        }

        // 交易状态

                 String trade_status = paramMap.get("trade_status");

        String out_trade_no = paramMap.get("out_trade_no");

        // true

        if (signVerified) {

            // TODO 验签成功后,按照支付结果异步通知中的描述,对支付结果中的业务内容进行二次校验,校验成功后在response中返回success并继续商户自身业务处理,校验失败返回failure

            if ("TRADE_SUCCESS".equals(trade_status) || "TRADE_FINISHED".equals(trade_status)) {

                // 但是,如果交易记录表中 PAID 或者 CLOSE  获取交易记录中的支付状态 通过outTradeNo来查询数据

                // select * from paymentInfo where out_trade_no=?

                PaymentInfo paymentInfo = paymentService.getPaymentInfo(out_trade_no, PaymentType.ALIPAY.name());

                if (paymentInfo.getPaymentStatus() == PaymentStatus.PAID.name() || paymentInfo.getPaymentStatus() == PaymentStatus.ClOSED.name()) {

                    return "failure";

                }

                // 正常的支付成功,我们应该更新交易记录状态           paymentService.paySuccess(out_trade_no,PaymentType.ALIPAY.name(), paramMap);

                return "success";

            }

  

        } else {

            // TODO 验签失败则记录异常日志,并在response中返回failure.

            return "failure";

        }

        return "failure";

    }

PaymentService

实现类

@Override

  public PaymentInfo getPaymentInfo(String out_trade_no, String name) {

    QueryWrapper<PaymentInfo> paymentInfoQueryWrapper = new QueryWrapper<>();

    paymentInfoQueryWrapper.eq("out_trade_no", outTradeNo
).eq("payment_type", paymentType);

    PaymentInfo paymentInfo = paymentInfoMapper.selectOne(paymentInfoQueryWrapper);

    return paymentInfo;

  

}
@Override

  public void paySuccess(String outTradeNo,String paymentType, Map<String,String> paramMap) {

    PaymentInfo paymentInfo = this.getPaymentInfo(outTradeNo, paymentType);

    if (paymentInfo.getPaymentStatus() == PaymentStatus.PAID.name() || paymentInfo.getPaymentStatus() == PaymentStatus.ClOSED.name()) {

        return;

    }

    PaymentInfo paymentInfoUpd = new PaymentInfo();

    // update paymentInfo set PaymentStatus = PaymentStatus.PAID ,CallbackTime = new Date() where out_trade_no = ?

    paymentInfoUpd.setPaymentStatus(PaymentStatus.PAID.name());

    paymentInfoUpd.setCallbackTime(new Date());

    paymentInfoUpd.setCallbackContent(paramMap.toString());

    this.updatePaymentInfo(outTradeNo,paymentInfoUpd);

    // 表示交易成功!

}
@Override
public void updatePaymentInfo(String outTradeNo, PaymentInfo paymentInfoUpd) {
    QueryWrapper<PaymentInfo> queryWrapper = new QueryWrapper<>();
    queryWrapper.eq("out_trade_no", outTradeNo);
    paymentInfoMapper.update(paymentInfoUpd,queryWrapper);
}

五、退款

直接在浏览器发起请求即可!

http://payment.gmall.com/refund?orderId=98

商户与客户协商一致的情况下,才可以退款!

// 发起退款!订单编号 http://payment.gmall.com/refund?orderId=118

  @RequestMapping("refund/{orderId}")
@ResponseBody

  public Result refund(@PathVariable(value = "orderId")Long orderId) {

    // 调用退款接口

    boolean flag = alipayService.refund(orderId);

    return Result.ok(flag);

}

boolean refund(String orderId);

@Override

  public boolean refund(Long orderId) {

 

    AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();

  

    OrderInfo orderInfo = orderFeignClient.getOrderInfo(orderId);

    HashMap<String, Object> map = new HashMap<>();

    map.put("out_trade_no", orderInfo.getOutTradeNo());

    map.put("refund_amount", orderInfo.getTotalAmount());

    map.put("refund_reason", "颜色浅了点");

    // out_request_no

    request.setBizContent(JSON.toJSONString(map));

    AlipayTradeRefundResponse response = null;

    try {

        response = alipayClient.execute(request);

    } catch (AlipayApiException e) {

        e.printStackTrace();

    }

    if (response.isSuccess()) {

        // 更新交易记录 : 关闭

        PaymentInfo paymentInfo = new PaymentInfo();

        paymentInfo.setPaymentStatus(PaymentStatus.ClOSED.name());

        paymentService.updatePaymentInfo(orderInfo.getOutTradeNo(), paymentInfo);

        return true;

    } else {

        return false;

    }

}

六、常见面试问题:

  1. 支付宝支付需要的参数?

公共的参数:

  支付网关\appID\请求数据类型(json)\编码\公钥和私钥\同步回调\异步回调

支付接口的参数:

  订单交易编号\金额\标题\销售产品码

退款接口:

     订单交易编号或者支付宝的交易号\退款金额

  1. 支付锁库存吗?

不锁,支付 用户在付钱呢,不能因为其他问题导致用户付钱失败.

  1. 支付日志表(信息表) 记录了什么东西? (主要是看表里的字段)

作用:为了和支付宝那边 对账

订单交易编号\金额\支付类型(支付宝、微信)、支付宝交易号、回调时间和回调内容、支付状态

猜你喜欢

转载自blog.csdn.net/leader_song/article/details/132113137
今日推荐