乐趣区

关于springboot:SpringBoot网页支付宝支付对接流程

场景:
针对 PC 网站领取场景

一、支付宝开放平台设置

1、登录支付宝开放平台

2、进入控制台,在【我的利用】抉择【网页 & 挪动利用】,而后点击【创立利用】

3、填写好响应的利用信息,点击【确认创立】

4、创立好之后,在控制台进入利用,增加“ 电脑网站领取能力 ”

阐明:增加这个能力,会须要进行填写签约信息,按着步骤走就行,不过填写的网站必须和企业支付宝的营业执照统一

5、点击左侧【利用信息】,进行设置开发信息,只设置了接口加签

5.1、设置接口加签形式,有公钥证书和公钥两个抉择,个别抉择公钥

5.2、而后须要生成密钥,能力生成支付宝公钥,生成密钥也有三种形式:

第一种:下载密钥生成工具
1、下载密钥生成工具:https://opendocs.alipay.com/o…
windows 版本电脑下载 windows 版本工具。
mac 版本电脑下载 mac_osx 版本工具。

2、运行装置密钥生成工具
点击下载的“AlipayDevelopmentAssistant-1.0.1.exe”(即支付宝开放平台凋谢助手)进行装置并运行。
阐明:本工具只会记录上传点击事件操作行为,不会记录上传用户的任何用户信息以及公私钥等敏感信息。

3、生成密钥
抉择密钥格局和密钥长度,点击“生成密钥”进行密钥生成。
(1)密钥长度
RSA2:密钥长度为 2048 位。
注:开放平台从 2018 年 1 月 5 日开始创立的利用都没有 RSA 密钥的设置入口,只能上传 RSA2 格局密钥

RSA:密钥长度为 1024 位。
国密:目前暂不反对国密的加签形式,即便获取后也无奈设置加签。

(2)密钥格局
PKCS8:JAVA 开发语言实用。
PKCS1:非 JAVA 开发语言实用。

4、配置密钥
开放平台 (open.alipay.com) 抉择要上传的利用,点击利用进入具体页,抉择利用信息内的“接口加签形式”进行设置。

如利用未上线,须要抉择概览外面的接口加签形式进行设置。
(1)利用私钥:开放平台没有上传设置的地位,须要本人进行保留并设置到代码中,且因为其波及资金平安不能将其提供给别人,若不小心失落或泄露,请及时进行更新批改。
(2)利用公钥:须要将其传入开放平台利用中(每次更换密钥时都要将其从新上传开放平台),如图:

5、接口中以及支付宝开放平台配置密钥
生成密钥后切记成对妥善保存,防止测试时因为公私钥不匹配导致签名验签等一系列不必要的谬误产生。

(1)正式环境中配置密钥
开发技术人员在接口中配置正式环境配置密钥,需抉择要上传的利用,点击利用进入具体页,抉择利用信息内的“接口加签形式”进行设置。

(2)沙箱环境中配置密钥
沙箱环境配置密钥,需抉择在沙箱利用中,抉择利用信息内的“RSA2(SHA256)密钥(举荐)”进行设置。

沙箱环境利用配置点击沙箱利用理解。

第二种:web 在线生成密钥
1、生成密钥
1.1、关上 Web 在线加密(无下载,新上线)-> 抉择“生成密钥”-> 抉择“保留密钥”。
1.2、抉择密钥格局和密钥长度,点击“生成密钥”进行密钥生成。
(1)密钥长度
RSA2:密钥长度为 2048 位。
注:开放平台从 2018 年 1 月 5 日开始创立的利用都没有 RSA 密钥的设置入口,只能上传 RSA2 格局密钥
RSA:密钥长度为 1024 位。
(2)密钥格局
PKCS8:JAVA 开发语言实用。
PKCS1:非 JAVA 开发语言实用。
2、配置密钥
配置密钥流程与“密钥生成工具生成密钥”的配置密钥流程雷同。
将利用公钥上传到开放平台利用的接口加签中,利用私钥请放在接口代码私钥地位。

第三种:应用 OpenSSL 生成
除了应用支付宝提供的一键生成工具外,也能够应用 OpenSSL 工具命令生成密钥。

应用 OpenSSL 工具命令生成,命令语句如下:
1、生成 RSA2 私钥(2048 位)命令:genrsa -out app_private_key.pem 2048;

2、把私钥转换成 PKCS8 格局并输入新文件命令:pkcs8 -topk8 -inform PEM -in app_private_key.pem -outform PEM -nocrypt -out app_private_key_pkcs8.pem;

3、生成公钥命令:rsa -in app_private_key.pem -pubout -out app_public_key.pem;
之后把生成的公钥上传给支付宝,并获取支付宝公钥(ALIPAY_PUBLIC_KEY)。

具体见应用 OpenSSL 工具生成密钥在线文档。

注:生成密钥后切记成对妥善保存,若不小心失落或泄露,请及时进行生成生成并从新上传到支付宝开放平台,避免出现损失。

6、配置好开发信息,而后再编辑利用信息,公布上线,即可开始开发正式环境

二、Springboot 代码编写

1、首先创立支付宝领取工具类

/**
 * 支付宝领取工具类
 */
public class ALiPayUtils {
    /*====== 沙箱环境 =======*/
    // 沙箱网关
//    private static final String GATEWAY_URL = "https://openapi.alipaydev.com/gateway.do";
    // 沙箱 APPID
//    private static final String APP_ID = "换成本人沙箱 APPID";
    // 本人利用私钥
//    private static final String PRIVATE_KEY = "生成的利用私钥";
    // 支付宝公钥(非应用公钥)
//    private static final String PUBLIC_KEY = "生成的支付宝公钥";
    // 沙箱领取实现跳转页面
    private static final String RETURN_URL = PropertiesValues.getPropertiesValue("pay.service.return_url", "application.properties");
    // 沙箱领取实现回调接口
    private static final String NOTIFY_URL = PropertiesValues.getPropertiesValue("pay.service.notify_url", "application.properties");

    /*======== 根底数据 ===========*/
    // 网关
    private static final String GATEWAY_URL = "https://openapi.alipay.com/gateway.do";
    //APPID
    public static final String APP_ID = "换成本人正式 APPID";
    // 本人利用私钥
    private static final String PRIVATE_KEY = "生成的利用私钥";
    // 支付宝公钥(非应用公钥)
    private static final String PUBLIC_KEY = "生成的支付宝公钥";
    // 返回数据格式
    private static final String FORMAT = "json";
    // 编码类型
    private static final String CHART_TYPE = "utf-8";
    // 签名类型
    private static final String SIGN_TYPE = "RSA2";

    /* 领取销售产品码, 目前支付宝只反对 FAST_INSTANT_TRADE_PAY*/
    public static final String PRODUCT_CODE = "FAST_INSTANT_TRADE_PAY";

    private static AlipayClient alipayClient = null;

    /*===== 商户对应数据 =====*/
    // 商户 PID
    public static final String PID = "2088441150504125";

    /*======== 相应接口 =========*/
    // 领取实现跳转页面
//    private static final String RETURN_URL = "";
    // 领取实现回调接口
//    private static final String NOTIFY_URL = "";

    public ALiPayUtils(){if(alipayClient == null){
            alipayClient = new DefaultAlipayClient(
                    GATEWAY_URL,
                    APP_ID,
                    PRIVATE_KEY,
                    FORMAT,
                    CHART_TYPE,
                    PUBLIC_KEY,
                    SIGN_TYPE);
        }
    };

    /*================PC 网页领取 ====================*/
    /**
     * 对立下单并调用领取页面接口
     * @param outTradeNo
     * @param totalAmount
     * @param subject
     * @return
     */
    public Map<String, Object> placeOrderAndPayForPCWeb(String outTradeNo, float totalAmount, String subject){AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
        request.setNotifyUrl(RETURN_URL);
        request.setReturnUrl(NOTIFY_URL);
        JSONObject bizContent = new JSONObject();
        bizContent.put("out_trade_no", outTradeNo);
        bizContent.put("total_amount", totalAmount);
        bizContent.put("subject", subject);
        bizContent.put("product_code", PRODUCT_CODE);
        //bizContent.put("time_expire", "2022-08-01 22:00:00");

        //// 商品明细信息,按需传入
        //JSONArray goodsDetail = new JSONArray();
        //JSONObject goods1 = new JSONObject();
        //goods1.put("goods_id", "goodsNo1");
        //goods1.put("goods_name", "子商品 1");
        //goods1.put("quantity", 1);
        //goods1.put("price", 0.01);
        //goodsDetail.add(goods1);
        //bizContent.put("goods_detail", goodsDetail);

        //// 扩大信息,按需传入
        //JSONObject extendParams = new JSONObject();
        //extendParams.put("sys_service_provider_id", "2088511833207846");
        //bizContent.put("extend_params", extendParams);

        request.setBizContent(bizContent.toString());
        AlipayTradePagePayResponse response = null;
        try {response = alipayClient.pageExecute(request);
        } catch (AlipayApiException e) {e.printStackTrace();
        }
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("isSuccess", response.isSuccess());
        if(response.isSuccess()){System.out.println("调用胜利");
            System.out.println(JSON.toJSONString(response));
            resultMap.put("body", response.getBody());
        } else {System.out.println("调用失败");
            System.out.println(response.getSubMsg());
            resultMap.put("subMsg", response.getSubMsg());
        }
        return resultMap;
    }

    /**
     * 交易订单查问
     * @param out_trade_no
     * @return
     */
    public Map<String, Object> tradeQueryForPCWeb(String out_trade_no){AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
        JSONObject bizContent = new JSONObject();
//        bizContent.put("out_trade_no", out_trade_no);
        bizContent.put("trade_no", out_trade_no);
        request.setBizContent(bizContent.toString());
        AlipayTradeQueryResponse response = null;
        try {response = alipayClient.execute(request);
        } catch (AlipayApiException e) {e.printStackTrace();
        }
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("isSuccess", response.isSuccess());
        if(response.isSuccess()){System.out.println("调用胜利");
            System.out.println(JSON.toJSONString(response));
            resultMap.put("status", response.getTradeStatus());
        } else {System.out.println("调用失败");
            System.out.println(response.getSubMsg());
            resultMap.put("subMsg", response.getSubMsg());
        }
        return resultMap;
    }

    /**
     * 验证签名是否正确
     * @param sign
     * @param content
     * @return
     */
    public static boolean CheckSignIn(String sign, String content){
        try {return AlipaySignature.rsaCheck(content, sign, PUBLIC_KEY, CHART_TYPE, SIGN_TYPE);
        } catch (AlipayApiException e) {e.printStackTrace();
        }
        return false;
    }

    /**
     * 将异步告诉的参数转化为 Map
     * @return
     */
    public static Map<String, String> paramstoMap(HttpServletRequest request) throws UnsupportedEncodingException {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);
        }
        return params;
    }
}

2、编写下单接口以及回调解决接口

@RestController
@CrossOrigin
@RequestMapping("lcAliPay")
public class LcALiPayController {

    @Autowired
    LcAliPayService lcAliPayService;

    @Autowired
    LcGoodsOrderService lcGoodsOrderService;

    /*==========PC 网页领取 ============*/
    /*=== 支付宝 ===*/

    /**
     * 对立下单并调用领取页面,PC
     * @param lcGoodsOrder
     * @return
     */
    @PostMapping("/tradeOrderForPCWeb")
    @NoVerify
    public ResultMap tradeOrderForPCWeb(@RequestBody LcGoodsOrder lcGoodsOrder){return lcAliPayService.tradeOrderForPCWeb(lcGoodsOrder);
    }

    /**
     * 领取实现回调接口,PC
     * @return
     */
    @RequestMapping("/notifyForPCWeb")
    @NoVerify
    public String notifyForPCWeb(HttpServletRequest request){
        try {Map<String, String> map = ALiPayUtils.paramstoMap(request);
            String tradeNo = map.get("trade_no");
//            Map<String, Object> payData = aLiPayUtils.tradeQueryForPCWeb(tradeNo);
            String sign = map.get("sign");
            String content = AlipaySignature.getSignCheckContentV1(map);
            boolean signVerified = ALiPayUtils.CheckSignIn(sign, content);
            // 验证业务数据是否统一
            if(!lcAliPayService.checkData(map)){System.out.println("返回业务数据验证失败,订单:" + tradeNo);
                return "fail";
            }
            // 签名验证胜利
            if(signVerified){System.out.println("支付宝签名验证胜利,订单:" + tradeNo);
                // 验证领取状态
                String tradeStatus = request.getParameter("trade_status");
                if(tradeStatus.equals("TRADE_SUCCESS")){System.out.println("领取胜利,订单:"+tradeNo);
                    LcGoodsOrder lcGoodsOrder = new LcGoodsOrder();
                    lcGoodsOrder.setTradeNo(map.get("trade_no"));
                    lcGoodsOrder.setSellerId(map.get("seller_id"));
                    lcGoodsOrder.setOrderStatus("2");// 领取胜利
                    lcGoodsOrderService.updateById(lcGoodsOrder);
                    return "success";
                }else{System.out.println("领取失败,订单:" + tradeNo);
                    return "fail";
                }
            }else{System.out.println("签名验证失败,订单:" + tradeNo);
            }
        } catch (UnsupportedEncodingException e) {e.printStackTrace();
        } catch (IOException e) {e.printStackTrace();
        }
        return "fail";
    }
}

3、service 层代码

@Service
public class LcAliPayServiceImpl implements LcAliPayService {

    @Resource
    LcGoodsOrderMapper lcGoodsOrderMapper;
    // 日志对象
    private static Logger logger = LoggerFactory.getLogger(LcAliPayServiceImpl.class);

    @Override
    public ResultMap tradeOrderForPCWeb(LcGoodsOrder lcGoodsOrder) {logger.info("【申请开始 - 在线购买 - 交易创立】********* 对立下单开始 *********");
        ALiPayUtils aLiPayUtils = new ALiPayUtils();
        String tradeNo = ALiPayUtils.generateOrderNum();
        Map<String, Object> map = aLiPayUtils.placeOrderAndPayForPCWeb(tradeNo, Float.parseFloat(lcGoodsOrder.getTotalAmount()), lcGoodsOrder.getSubject());
        ResultMap resultMap = new ResultMap();
        if(Boolean.parseBoolean(String.valueOf(map.get("isSuccess")))){logger.info("【申请开始 - 在线购买 - 交易创立】对立下单胜利,开始保留订单数据");
            resultMap.setCode("200");
            resultMap.setData(map.get("body"));
            String lcGoodsOrderId = UUID.getRandom();
            lcGoodsOrder.setOrderId(lcGoodsOrderId);
            lcGoodsOrder.setOrderCreateTime(DateTime.getNowDateStamp());
            lcGoodsOrder.setOutTradeNo(tradeNo);
            lcGoodsOrder.setOrderStatus("1");// 订单状态
            lcGoodsOrder.setProductCode(ALiPayUtils.PRODUCT_CODE);
            // 保留订单信息
            int count = lcGoodsOrderMapper.insert(lcGoodsOrder);
            if(count > 0){logger.info("【胜利 - 在线购买 - 交易创立】保留订单数据胜利,增加条数:{}", count);
                resultMap.setCode(ResponseCodeUtis.SERVER_TRUE_200);
                resultMap.setMsg(ResponseCodeUtis.MSG_TRUE_InsertById);
                resultMap.setData(map.get("body"));
            }else{logger.error("【失败 - 在线购买 - 交易创立】保留订单数据失败");
                resultMap.setCode(ResponseCodeUtis.SERVER_ERROR_500);
                resultMap.setMsg("增加数据失败");
            }
            logger.info("【申请胜利 - 在线购买 - 交易创立】********* 对立下单完结 *********");
            return resultMap;
        }else{resultMap.setCode("501");
            resultMap.setMsg(String.valueOf(map.get("subMsg")));
            logger.info("【失败:申请失败 - 在线购买 - 交易创立】********* 对立下单完结 *********");
        }
        return resultMap;
    }

    @Override
    public boolean checkData(Map<String, String> map) {logger.info("【申请开始 - 交易回调 - 订单确认】********* 校验订单确认开始 *********");
        LcGoodsOrder lcGoodsOrder2 = lcGoodsOrderMapper.selectByOutTradeNo(map.get("out_trade_no"));
        // 验证订单号是否精确,并且订单状态为待领取
        if(lcGoodsOrder2 != null && lcGoodsOrder2.getOrderStatus().equals("1")){float amount1 = Float.parseFloat(map.get("total_amount"));
            float amount2 = Float.parseFloat(lcGoodsOrder2.getTotalAmount());
            // 判断金额是否相等
            if(amount1 == amount2){
                // 验证收款商户 id 是否统一
                if(map.get("seller_id").equals(ALiPayUtils.PID)){
                    // 判断 appid 是否统一
                    if(map.get("app_id").equals(ALiPayUtils.APP_ID)){logger.info("【胜利:申请开始 - 交易回调 - 订单确认】********* 校验订单确认胜利 *********");
                        return true;
                    }
                }
            }
        }
        logger.info("【失败:申请开始 - 交易回调 - 订单确认】********* 校验订单确认失败 *********");
        return false;
    }
}

4、网站页面领取下单申请代码

/**
 * 调用支付宝领取接口
 * @param {Object} data
 */
function aliPay(data){
    var url = "服务器接口";
    $.ajax({
        url: url,
        type: 'post',
        data: JSON.stringify({
            payUserName: data.payUserName,
            payUserPhone: data.payUserPhone,
            payMessage: data.payMessage,
            goodsId: data.goodsId,
            totalAmount: data.totalAmount,
            subject: data.subject,
        }),
        contentType: "application/json",
        dataType: 'json',
        success: function(res){if(res.code == "200"){const div=document.createElement('divform');
                div.innerHTML=res.data;
                document.body.appendChild(div);
                document.forms[0].setAttribute('target', '_blank') 
                document.forms[0].submit();}else{alert("网络异样");
                alert(res.msg);
            }
        },
        fail: function(res){console.log("失败")
            console.log(res);
        }
      })
}

5、下单接口回调,解决返回表单解决问题

if(res.code == "200"){const div=document.createElement('divform');
    div.innerHTML=res.data;
    document.body.appendChild(div);
    document.forms[0].setAttribute('target', '_blank') 
    document.forms[0].submit();}else{alert("网络异样");
    alert(res.msg);
}

6、申请胜利图

退出移动版