支付宝商户代扣(免密签约,免密扣款)

商户代扣产品介绍 - 支付宝文档中心 (alipay.com)

签约接口

 由于前端使用的是钉钉小程序,无法直接跳转支付宝,所以使用pageExecute执行,将支付宝返回的链接封装成二维码返给前端,前端点击二维码扫码前往支付宝签约

/**
     * 调用支付宝签约接口
     */
    @ResponseBody
    @PostMapping("/alipaySignup")
    public AjaxResult alipaySignup(@RequestBody JnDingdingUser dingdingUser) throws AlipayApiException, IOException, WriterException {

        AlipayClient alipayClient = new DefaultAlipayClient(AliPayConfig.GATEWAYURL,
                AliPayConfig.APP_ID,
                AliPayConfig.MERCHANT_PRIVATE_KEY,
                "json",
                AliPayConfig.CHARSET,
                AliPayConfig.ALIPAY_PUBLIC_KEY,
                AliPayConfig.SIGN_TYPE);
        AlipayUserAgreementPageSignModel bizModel = new AlipayUserAgreementPageSignModel();

        /**
         * 个人签约产品码,商户和支付宝签约时确定。商户代扣场景固定 GENERAL_WITHHOLDING_P
         */
        bizModel.setPersonalProductCode("GENERAL_WITHHOLDING_P");
        /**
         * 销售产品码,商户签约的支付宝合同所对应的产品码。商户代扣场景固定为 GENERAL_WITHHOLDING
         */
        bizModel.setProductCode("GENERAL_WITHHOLDING");

        /**
         * 协议签约场景,商户和支付宝签约时确定,商户可咨询技术支持。本参数为空时默认为 DEFAULT|DEFAULT。更多支持场景参见 代扣场景码介绍。
         *
         * 注意: 当传入商户签约号 external_agreement_no 时,场景不能为默认值 DEFAULT|DEFAULT。
         */
        bizModel.setSignScene("INDUSTRY|CATERING");

        /**
         * 商户签约号,代扣协议中标示用户的唯一签约号(确保在商户系统中唯一)。
         格式规则:支持大写小写字母和数字,最长32位。
         商户系统按需传入,如果同一用户在同一产品码、同一签约场景下,签订了多份代扣协议,那么需要指定并传入该值。
         */
        bizModel.setExternalAgreementNo (dingdingUser.getUserid());

        /**
         * 用户在商户网站的登录账号,用于在签约页面展示,如果为空,则不展示
         */
        bizModel.setExternalLogonId(dingdingUser.getUserid());

        AccessParams accessParams = new AccessParams();
        accessParams.setChannel("ALIPAYAPP");
        bizModel.setAccessParams(accessParams);
        AlipayUserAgreementPageSignRequest request = new AlipayUserAgreementPageSignRequest();
        request.setBizModel(bizModel);
        // 异步通知服务器地址写在application.yml中
        String notifyUrl = severUrl+"/signupNotify";
        request.setNotifyUrl(notifyUrl);
        AlipayUserAgreementPageSignResponse response = alipayClient.pageExecute(request,"get");
        String url = response.getBody();
//        System.out.println(url);
        QRCodeWriter qrCodeWriter = new QRCodeWriter();
//      第一个参数为二维码的内容 第二个参数不变  第三 四 个参数依次为 宽高
        BitMatrix bitMatrix = qrCodeWriter.encode(url, BarcodeFormat.QR_CODE, 800, 800);
        File folder = new File(RuoYiConfig.getQRCodePath());
        if (!folder.exists() && !folder.isDirectory()) {
            folder.mkdirs();
        }
        String fileName = UUID.randomUUID().toString()+ ".png";
        String path = RuoYiConfig.getQRCodePath() +"/"+ fileName;
        MatrixToImageWriter.writeToPath(bitMatrix, "png",Paths.get(path));
//      处理图片路径返回给前端
        int dirLastIndex = RuoYiConfig.getProfile().length() + 1;
        String currentDir = StringUtils.substring(RuoYiConfig.getQRCodePath(), dirLastIndex);
        String pathFileName = Constants.RESOURCE_PREFIX + "/" + currentDir + "/" + fileName;



        if (response.isSuccess()) {
            return AjaxResult.success(pathFileName);
        } else {
            return AjaxResult.error("签约失败");
        }
    }

签约异步回调接口

主要返回参数:

external_logon_id 用户在商户系统中的id,签约时给的是什么返回的就是什么

alipay_logon_id 用户在支付宝中的登录账号,返回时带星号

alipay_user_id 支付宝用户号,纯数字

agreement_no 签约号,商户系统通过签约号发起免密扣款

返回值“success” 如果返回的不是success 支付宝会在24小时内不断发异步通知

 /**
     * 签约异步通知接口
     */
    @ResponseBody
    @PostMapping("/signupNotify")
    public String signupNotify(HttpServletRequest request) throws AlipayApiException, UnsupportedEncodingException {
        //获取支付宝POST过来反馈信息
        Map< String , String > params = new HashMap<>();
        Map requestParams = request.getParameterMap();
        System.out.println(requestParams);
        System.out.println("————————————————————");
        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] + ",";
            }
            //乱码解决,这段代码在出现乱码时使用。
            //valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
            params.put (name,valueStr);
        }
        System.out.println(params);
        //切记alipaypublickey是支付宝的公钥,请去open.alipay.com对应应用下查看。
        //boolean AlipaySignature.rsaCheckV1(Map<String, String> params, String publicKey, String charset, String sign_type)
        boolean flag = AlipaySignature.rsaCheckV1 (params,AliPayConfig.ALIPAY_PUBLIC_KEY, AliPayConfig.CHARSET,"RSA2");
        if (flag){
            System.out.println("验签成功,处理业务");
            String userid = params.get("external_logon_id");
            String alipayLogonId = params.get("alipay_logon_id");
            String agreementNo = params.get("agreement_no");
            String alipayUserId = params.get("alipay_user_id");
            return "success";
        }else {

            System.out.println("验签失败");
            return "fail";
        }
    }

同步代扣接口

同步代扣的response中会返回code,前端调用免密代扣时根据code返回结果给前端

code 结果码和处理方式

  • 根据公共返回参数中的 code,这笔交易可能有四种状态:支付成功(10000),支付失败(40004),等待用户付款(10003)和未知异常(20000)。

  • 对于 扣款失败 或 未知异常, 存在如下两种场景解决方案:

    • 针对耗时敏感的业务场景(如:免密支付,用户需要同步等待结果):建议商家优先使用 alipay.trade.query 接口查询交易状态(查询次数和间隔可依据商家场景),如果交易状态是成功,则无需特殊处理;如果交易状态是等待买家付款,则可以使用 alipay.trade.cancel 接口发起交易撤销。

    • 针对耗时不敏感的业务场景(如:周期续费代扣,无需用户同步等待结果):建议商家等待5分钟后,使用 alipay.trade.query 接口查询交易状态,若查询 4 次后依然返回等待用户付款,商户可以选择幂等重试代扣或通过 alipay.trade.cancel 接口发起交易撤销。

 public Map<String, String> dinguserPay(String userid, String totalAmount, String orderSn) throws AlipayApiException {

        AlipayClient alipayClient = new DefaultAlipayClient(AliPayConfig.GATEWAYURL,
                AliPayConfig.APP_ID,
                AliPayConfig.MERCHANT_PRIVATE_KEY,
                "json",
                AliPayConfig.CHARSET,
                AliPayConfig.ALIPAY_PUBLIC_KEY,
                AliPayConfig.SIGN_TYPE);
        Map<String, String> map = new HashMap<>();
       //根据userid找到user并获取agreementNo
        User user = userService.getUserById(userid);
        String agreementNo= user.getagreementNo();//获取签约号
        if (StringUtils.isEmpty(agreementNo)){
            map.put("status","fail");
            map.put("msg","未查询到支付宝信息,请先绑定支付宝再操作!");
            return map;
        }
        
        AlipayTradePayRequest request = new AlipayTradePayRequest();
        //获取异步通知服务器地址,再拼接具体业务通知地址
        String notifyUrl = url+"/payNotify";
        request.setNotifyUrl(notifyUrl);
        JSONObject bizContent = new JSONObject();

        bizContent.put("out_trade_no", orderSn);
        bizContent.put("total_amount", totalAmount);//支付金额
        bizContent.put("subject", "支付宝中显示的消费信息");
        bizContent.put("product_code", "GENERAL_WITHHOLDING");//商户代扣固定GENERAL_WITHHOLDING
        //协议信息
        JSONObject agreementParams = new JSONObject();
        agreementParams.put("agreement_no", agreementNo);
        bizContent.put("agreement_params", agreementParams);
        request.setBizContent(bizContent.toString());
        AlipayTradePayResponse response = alipayClient.execute(request);

        System.out.println(response);
        if(response.isSuccess()){
            String code = response.getCode();
            map.put("tradeNo",response.getTradeNo());
            //结果码10000,付款成功
            if (code.equals("10000")){
                map.put("msg",response.getMsg());
                map.put("status","success");
                return map;
            }
            //针对耗时敏感的业务场景
            //结果码10003等待用户付款,20000未知异常,等待一秒调用查询接口
            else if (code.equals("10003")||code.equals("20000")){
                try {
                    Thread.sleep(1000); //1000 毫秒,也就是1秒.
                    //调用查询接口
                    AlipayTradeQueryRequest queryRequest = new AlipayTradeQueryRequest();
                    JSONObject content = new JSONObject();
                    content.put("out_trade_no", orderSn);

                    request.setBizContent(content.toString());
                    AlipayTradeQueryResponse queryResponse = alipayClient.execute(queryRequest);
                    if(queryResponse.isSuccess()){
                        //查询到支付成功
                        if (queryResponse.getCode().equals("10000")){
                            map.put("msg",response.getMsg());
                            map.put("status","success");
                            return map;
                        }else if(queryResponse.getCode().equals("10003")){//查询为等待支付
                            //发起交易取消
                            AlipayTradeCancelRequest cancelRequest = new AlipayTradeCancelRequest();
                            JSONObject cancelContent = new JSONObject();
                            cancelContent.put("out_trade_no", orderSn);
                            request.setBizContent(cancelContent.toString());
                            AlipayTradeCancelResponse cancelResponse = alipayClient.execute(cancelRequest);
                            if(cancelResponse.isSuccess()){
                                map.put("status","fail");
                                map.put("msg","交易取消");
                                return map;
                            } else {
                                map.put("status","fail");
                                map.put("msg",cancelResponse.getMsg());
                                return map;
                            }
                        }

                    } else {
                        map.put("status","fail");
                        map.put("msg",queryResponse.getMsg());
                        return map;
                    }
                } catch(InterruptedException ex) {
                    Thread.currentThread().interrupt();
                }
            }else {//结果码40000,交易失败
                map.put("status","fail");
                map.put("msg",response.getSubMsg());
                return map;
            }

        } else {//response.isSuccess为false
            map.put("status","fail");
            map.put("msg",response.getSubMsg());
            return map;
        }
        map.put("status","fail");
        map.put("msg",response.getSubMsg());
        return map;
    }

免密代扣异步通知接口(以异步通知为准)

 /**
     * 扣款异步通知接口
     */
    @ResponseBody
    @PostMapping("/payNotify")
    public String payNotify(HttpServletRequest request) throws AlipayApiException, ParseException {
        //获取支付宝POST过来反馈信息
        Map< String , String > params = new HashMap<>();
        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] + ",";
            }
            //乱码解决,这段代码在出现乱码时使用。
            //valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
            params.put (name,valueStr);
        }
        //切记alipaypublickey是支付宝的公钥,请去open.alipay.com对应应用下查看。
        //boolean AlipaySignature.rsaCheckV1(Map<String, String> params, String publicKey, String charset, String sign_type)
        boolean flag = AlipaySignature.rsaCheckV1 (params,AliPayConfig.ALIPAY_PUBLIC_KEY, AliPayConfig.CHARSET,"RSA2");
        if (flag){
            //处理业务
            JnAlipayRecord alipayRecord = new JnAlipayRecord();
            alipayRecord.setTradeNo(params.get("trade_no"));
            alipayRecord.setOutTradeNo(params.get("out_trade_no"));
            alipayRecord.setSellerEmail(params.get("seller_email"));
            alipayRecord.setSubject(params.get("subject"));
            alipayRecord.setBuyerId(params.get("buyer_id"));
            alipayRecord.setInvoiceAmount(params.get("invoice_amount"));
            alipayRecord.setNotifyId(params.get("notify_id"));
            alipayRecord.setFundBillList(params.get("fund_bill_list"));
            alipayRecord.setNotifyType(params.get("notify_type"));
            alipayRecord.setTradeStatus(params.get("trade_status"));
            alipayRecord.setReceiptAmount(params.get("receipt_amount"));
            alipayRecord.setAppId(params.get("app_id"));
            alipayRecord.setBuyerPayAmount(params.get("buyer_pay_amount"));
            alipayRecord.setSellerId(params.get("seller_id"));
            alipayRecord.setTotalAmount(params.get("total_amount"));
            alipayRecord.setBuyerLogonId(params.get("buyer_logon_id"));

            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String gmtCreate = params.get("gmt_create");
            String gmtPayment = params.get("gmt_payment");
            String notifyTime = params.get("notify_time");

            alipayRecord.setGmtCreate(sdf.parse(gmtCreate));
            alipayRecord.setGmtPayment(sdf.parse(gmtPayment));
            alipayRecord.setNotifyTime(sdf.parse(notifyTime));
            
            jnAlipayRecordService.insertJnAlipayRecord(alipayRecord);
            return "success";
        }else {
            return "fail";
        }
    }

  • 1
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值