网站首页> 博客> 支付宝沙箱环境对接(当面付)

支付宝沙箱环境对接(当面付)

好文 3323
luomingyi
luomingyi 2018-01-02
收藏

支付宝沙箱环境对接(当面付)

第一步:

好好阅读以下的文档:

沙箱登录:https://openhome.alipay.com/platform/appDaily.htm

沙箱环境使用说明:https://doc.open.alipay.com/doc2/detail.htm?treeId=200&articleId=105311&docType=1

如何使用沙箱环境:https://support.open.alipay.com/support/hotProblemDetail.htm?spm=a219a.7386793.0.0.uS5uZ6&id=251932&tagId=100248

当面付产品介绍:https://doc.open.alipay.com/docs/doc.htm?spm=a219a.7629140.0.0.hV5Clx&treeId=193&articleId=105072&docType=1

扫码支付接入指引:https://doc.open.alipay.com/docs/doc.htm?spm=a219a.7629140.0.0.Ia6Wqy&treeId=193&articleId=106078&docType=1

当面付快速接入:https://doc.open.alipay.com/docs/doc.htm?spm=a219a.7629140.0.0.bROnXf&treeId=193&articleId=105170&docType=1

当面付接入必读:https://doc.open.alipay.com/docs/doc.htm?spm=a219a.7629140.0.0.hV5Clx&treeId=193&articleId=105322&docType=1

当面付进阶功能:https://doc.open.alipay.com/docs/doc.htm?spm=a219a.7629140.0.0.YFmkxI&treeId=193&articleId=105190&docType=1

当面付异步通知-仅用于扫码支付:https://doc.open.alipay.com/docs/doc.htm?spm=a219a.7629140.0.0.BykVSR&treeId=193&articleId=103296&docType=1

当面付SDK&DEMOhttps://support.open.alipay.com/docs/doc.htm?spm=a219a.7386797.0.0.k0rwWc&treeId=193&articleId=105201&docType=1

服务端SDKhttps://doc.open.alipay.com/doc2/detail?treeId=54&articleId=103419&docType=1

生成RSA密钥:https://doc.open.alipay.com/docs/doc.htm?treeId=291&articleId=105971&docType=1

线上创建应用说明:https://doc.open.alipay.com/doc2/detail.htm?treeId=200&articleId=105310&docType=1#s0

 

 

第二步:

下载好支付宝提供的当面付demo,将支付宝沙箱环境的应用网关、UIDAPPID、私钥、公钥以及对应的支付宝秘钥输到zfbinfo.properties文件中,把demo调通。

 

 

第三步:

沙箱环境进行测试,支付宝会回调数据回来,所以需要将本机地址与外网进行连接,需要用到内网穿透,可以用natapp工具。

 

 

第四步:

集成到项目中,controller里主要有三个actionalipay()alipay_callbackquery_order_pay_status,alipay_callback是支付宝回调调用的,alipay是支付生成二维码等、query_order_pay_status是查询订单状态,判断是否支付,然后让前端作出反应用的:

 

 

代码:

private static final Logger logger = LoggerFactory.getLogger(OrderController.class);

@Autowired

private OrderService orderService;


/***

 * 支付

 * @param session

 * @param orderNo

 * @param request

 * @return

 */

@RequestMapping("alipay")

@ResponseBody

public ServerData alipay(HttpSession session,Long orderNo,HttpServletRequest request){

User user =(User) session.getAttribute("user");

System.out.println("==========================="+user);

//通过request拿到上下门对象,取到upload文件夹,将生成的二维码放到upload里

//取到upload路径

String path = request.getSession().getServletContext().getRealPath("upload");

return orderService.pay(orderNo, path, user.getId());

}



/***

 * 支付宝回调(由支付宝来调的)

 * @param request

 * @return

 */

@RequestMapping("alipay_callback")

@ResponseBody

public Object alipay_callback(HttpServletRequest request){

Map params = new HashMap();


//支付宝返回的数据都是放在getParameterMap()里

Map requestParams = request.getParameterMap();


//通过迭代器的方法将key和vaule取出来

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]+",";

}

params.put(name, valueStr);

}

//打印一下日志

logger.info("支付宝回调,sign:{},trade_statu:{},参数:{}",params.get("sign"),params.get("trade_status"),params.toString());


//验证回调的正确性,是不是支付宝发过来的,还要避免重复通知

params.remove("sign_type");

try {

boolean alipayRSACheckedV2 = AlipaySignature.rsaCheckV2(params, Configs.getAlipayPublicKey(), "utf-8", Configs.getSignType());

if(!alipayRSACheckedV2){

return ServerData.createByErrorMsg("非法请求");

}

} catch (AlipayApiException e) {

logger.error("支付宝回调异常!",e);

}


//todo,根据支付宝文档要求,需要验证各种数据

ServerData response = orderService.alicallback(params);

if(response.isSuccess()){

return Const.AlipayCallback.RESPONSE_SUCCESS;//支付宝规定要返回               success或者failed

}

return Const.AlipayCallback.RESPONSE_FAILED;

}



/***

 * 查询订单支付状态

 * @param session

 * @param orderNo

 * @return

 */

@RequestMapping("query_order_pay_status")

@ResponseBody

public ServerData queryOrderPayStatus(HttpSession session,Long orderNo){

User user =(User) session.getAttribute("user");

if(user == null){

return ServerData.createByErrorMsg("请登录");

}

ServerData response = orderService.queryOrderPayStatus(orderNo, user.getId());

if(response.isSuccess()){

return ServerData.createBySuccessData(true);

}

//不需要报错,直接返回false

return ServerData.createBySuccessData(false);

}

 

 

 

实现类的代码:

private static Logger logger = LoggerFactory.getLogger(OrderServiceImpl.class);


@Autowired

private OrderMapper orderMapper;

@Autowired

private OrderItemMapper orderItemMapper;

@Autowired

private PayInfoMapper payInfoMapper;


 // 支付宝当面付2.0服务

    private static AlipayTradeService   tradeService;

    

    static{

      /** 一定要在创建AlipayTradeService之前调用Configs.init()设置默认参数

         *  Configs会读取classpath下的zfbinfo.properties文件配置信息,如果找不到该文件则确认该文件是否在classpath目录

         */

        Configs.init("zfbinfo.properties");

 

        /** 使用Configs提供的默认参数

         *  AlipayTradeService可以使用单例或者为静态成员对象,不需要反复new

         */

    }


    // 支付宝简单打印应答

    private void dumpResponse(AlipayResponse response) {

        if (response != null) {

            logger.info(String.format("code:%s, msg:%s", response.getCode(), response.getMsg()));

            if (StringUtils.isNotEmpty(response.getSubCode())) {

                logger.info(String.format("subCode:%s, subMsg:%s", response.getSubCode(),

                    response.getSubMsg()));

            }

            logger.info("body:" + response.getBody());

        }

    }

    

    /**

     * 通过orderNo和userId拿到orderItem,将orderItem里面的属性写到支付宝提供的接口里面,生成二维码,再将二维码上传到FTP服务器,且返回给前端,供用户扫码付款

     */

public ServerData pay(Long orderNo,String path,Integer userId){

Map resultMap = new HashMap();

//校验通过orderNo和userId的订单存不存在

Order order = orderMapper.selectOrderByOrderNoUserId(orderNo, userId);

if(order == null){

return ServerData.createByErrorMsg("没有该订单");

}

resultMap.put("orderNo", order.getOrderNo().toString());


/**进行支付宝对接*/

// (必填) 商户网站订单系统中唯一订单号,64个字符以内,只能包含字母、数字、下划线,

        // 需保证商户系统端不能重复,建议通过数据库sequence生成,

        /*String outTradeNo = "tradeprecreate" + System.currentTimeMillis()

                            + (long) (Math.random() * 10000000L);*/

String outTradeNo = order.getOrderNo().toString();

 

        // (必填) 订单标题,粗略描述用户的支付目的。如“xxx品牌xxx门店当面付扫码消费”

//StringBuilder()返回的是一个Object类型的

        String subject = new StringBuilder().append("loveshop扫码支付,订单号:").append(outTradeNo).toString();

 

        // (必填) 订单总金额,单位为元,不能超过1亿元

        // 如果同时传入了【打折金额】,【不可打折金额】,【订单总金额】三者,则必须满足如下条件:【订单总金额】=【打折金额】+【不可打折金额】

        String totalAmount = order.getPayment().toString();

 

        // (可选) 订单不可打折金额,可以配合商家平台配置折扣活动,如果酒水不参与打折,则将对应金额填写至此字段

        // 如果该值未传入,但传入了【订单总金额】,【打折金额】,则该值默认为【订单总金额】-【打折金额】

        String undiscountableAmount = "0";

 

        // 卖家支付宝账号ID,用于支持一个签约账号下支持打款到不同的收款账号,(打款到sellerId对应的支付宝账号)

        // 如果该字段为空,则默认为与支付宝签约的商户的PID,也就是appid对应的PID

        String sellerId = "";

 

        // 订单描述,可以对交易或商品进行一个详细地描述,比如填写"购买商品2件共15.00元"

        String body = new StringBuilder().append("订单:").append(outTradeNo).append("  购买金额为:").append(totalAmount).toString();

 

        // 商户操作员编号,添加此参数可以为商户操作员做销售统计

        String operatorId = "test_operator_id";

 

        // (必填) 商户门店编号,通过门店号和商家后台可以配置精准到门店的折扣信息,详询支付宝技术支持

        String storeId = "test_store_id";

 

        // 业务扩展参数,目前可添加由支付宝分配的系统商编号(通过setSysServiceProviderId方法),详情请咨询支付宝技术支持

        ExtendParams extendParams = new ExtendParams();

        extendParams.setSysServiceProviderId("2088100200300400500");

 

        // 支付超时,定义为120分钟

        String timeoutExpress = "120m";

 

        // 商品明细列表,需填写购买商品详细信息,

        List goodsDetailList = new ArrayList();

        //同个用户同个订单下,可以有多个明细

        List orderItemList = orderItemMapper.selectOrderItemByOrderNoUserId(orderNo, userId);

        for(OrderItem orderItem: orderItemList){

         // 创建一个商品信息,参数含义分别为商品id(使用国标)、名称、单价(单位为分)、数量,如果需要添加商品类别,详见GoodsDetail

            GoodsDetail goods1 = GoodsDetail.newInstance(orderItem.getProductId().toString(),

              orderItem.getProductName(),

              BigDecimalUtil.mul(orderItem.getCurrentUnitPrice().doubleValue(), new Double(100).doubleValue()).longValue(),

              orderItem.getQuantity());

            goodsDetailList.add(goods1);

           

        }

        /*// 创建一个商品信息,参数含义分别为商品id(使用国标)、名称、单价(单位为分)、数量,如果需要添加商品类别,详见GoodsDetail

        GoodsDetail goods1 = GoodsDetail.newInstance("goods_id001", "xxx小面包", 1000, 1);

        // 创建好一个商品后添加至商品明细列表

        goodsDetailList.add(goods1);

 

        // 继续创建并添加第一条商品信息,用户购买的产品为“黑人牙刷”,单价为5.00元,购买了两件

        GoodsDetail goods2 = GoodsDetail.newInstance("goods_id002", "xxx牙刷", 500, 2);

        goodsDetailList.add(goods2);*/

 

        // 创建扫码支付请求builder,设置请求参数

        AlipayTradePrecreateRequestBuilder builder = new AlipayTradePrecreateRequestBuilder()

            .setSubject(subject).setTotalAmount(totalAmount).setOutTradeNo(outTradeNo)

            .setUndiscountableAmount(undiscountableAmount).setSellerId(sellerId).setBody(body)

            .setOperatorId(operatorId).setStoreId(storeId).setExtendParams(extendParams)

            .setTimeoutExpress(timeoutExpress)

            .setNotifyUrl(PropertiesUtil.getPropertyMethod("alipay.callback.url"))//支付宝服务器主动通知商户服务器里指定的页面http路径,根据需要设置

            .setGoodsDetailList(goodsDetailList);

       

        tradeService = new AlipayTradeServiceImpl.ClientBuilder().build();

 

        AlipayF2FPrecreateResult result = tradeService.tradePrecreate(builder);

        switch (result.getTradeStatus()) {

            case SUCCESS:

               logger.info("支付宝预下单成功: )");

 

                AlipayTradePrecreateResponse response = result.getResponse();

                dumpResponse(response);

                

                //创建目录(),如果目录不存在,则创建并赋予写的权限;如果目录存在,就不用创建

                File folder = new File(path);

                if(!folder.exists()){

                 //赋予目录写的权限

                 folder.setWritable(true);

                 folder.mkdirs();

                }

                

                //下单成功之后,生成二维码,将二维码上传到FTP服务器上,再把二维码返回给前端

                // 需要修改为运行机器上的路径

                String qrPath = String.format(path+"/qr-%s.png",response.getOutTradeNo());

                logger.info("qrPath:" + qrPath);

                //获取到二维码文件名

                String qrFileName = String.format("qr-%s.png",response.getOutTradeNo());

                ZxingUtils.getQRCodeImge(response.getQrCode(), 256, qrPath);

                //这里不太懂

                File targetFile = new File(path,qrFileName);

                FTPUtil.uploadFile(Lists.newArrayList(targetFile));

                String qrUrl = PropertiesUtil.getPropertyMethod("ftp.server.http.prefix")+targetFile.getName();

                resultMap.put("qrUrl", qrUrl);

                return ServerData.createBySuccessData(resultMap);

                

            case FAILED:

                logger.error("支付宝预下单失败!!!");

                return ServerData.createByErrorMsg("支付宝预下单失败!!!");

 

            case UNKNOWN:

                logger.error("系统异常,预下单状态未知!!!");

                return ServerData.createByErrorMsg("系统异常,预下单状态未知!!!");

 

            default:

                logger.error("不支持的交易状态,交易返回异常!!!");

                return ServerData.createByErrorMsg("不支持的交易状态,交易返回异常!!!");

        }

     

}


public ServerData alicallback(Map params){

//这是从支付宝回调回来的数据(request.getParameterMap())

//params.get("out_trade_no")返回的外部订单号就是我们的内部订单号

Long orderNo = Long.parseLong(params.get("out_trade_no"));

String tradeNo = params.get("trade_no");

String tradeStatu = params.get("trade_status");

Order order = orderMapper.selectOrderByOrderNo(orderNo);

if(order == null){

return ServerData.createByErrorMsg("非loveshop网站的订单,回调忽略");

}

/*CANCELED(0,"已取消"),

NO_PAY(10,"未支付"),

PAID(20,"已支付"),

SHIPPED(40,"已发货"),

ORDER_SUCCESS(50,"订单完成"),

ORDER_CLOSE(60,"订单关闭");*/

if(order.getStatus() >= Const.OrderStatuEnum.PAID.getCode()){

//说明这个订单在之前已经付款了,支付宝重复调用

return ServerData.createBySuccessMsg("支付宝重复调用");

}

if(Const.AlipayCallback.TRADE_STATU_TRADE_SUCCESS.equals(tradeStatu)){

order.setStatus(Const.OrderStatuEnum.PAID.getCode());

order.setPaymentTime(DateTimeUtil.strToDate(params.get("gmt_payment")));

orderMapper.updateByPrimaryKeySelective(order);

}

//填充支付信息表

PayInfo payInfo = new PayInfo();

payInfo.setUserId(order.getUserId());

payInfo.setOrderNo(order.getOrderNo());

payInfo.setPayPlatform(Const.PayPlatform.ALIPAY.getCode());

payInfo.setPlatformNumber(tradeNo);//支付宝支付流水号

payInfo.setPlatformStatus(tradeStatu);

payInfoMapper.insert(payInfo);

return ServerData.createBySuccess();


}

 


public ServerData queryOrderPayStatus(Long orderNo,Integer userId){

Order order = orderMapper.selectOrderByOrderNoUserId(orderNo, userId);

if(order == null){

return ServerData.createByErrorMsg("非loveshop网站的订单,回调忽略");

}

if(order.getStatus() >= Const.OrderStatuEnum.PAID.getCode()){

//支付成功

return ServerData.createBySuccess();

}

//支付失败

return ServerData.createByError();


}

 

大概这是这些,有问题可以来与我联系qq:1210025283


  • 没有任何评论
个评论
luomingyi

luomingyi (青铜)

10金币 (1)粉丝 (2)源码

 

签到活跃榜 连续签到送额外金币
最新博客
基于SSM框架CRM客户管理系统 36
基于SSM框架CRM客户管理系统 39
Redis 虽然很牛,但不懂使用规范就糟了! 128
打广告 205
JSON PHP 358
CSS 教程 310
IT行业的发展应该发展宽度还是应该发展深度???? 247
eclipse部署项目后,在tomcat中的webapps文件夹下没有项目文件 431
SpringMVC标签form:options使用说明 406
springboot 232