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

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

好文 6453
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金币 (2)粉丝 (2)源码

 

加入微信群,不定期分享源码和经验
签到活跃榜 连续签到送额外金币
最新博客
校园跑腿系统外卖系统软件平台大学生创业平台搭建 667
壹脉销客智能名片CRM系统小程序可二开源码交付部署 654
为啥没搞了 1025
Nginx 的 5 大应用场景,太实用了! 1248
CentOS 8-stream 安装Postgresql 详细教程 1492
JAVA智慧校园管理系统小程序源码 电子班牌 Sass 模式 1232
Java智慧校园系统源码 智慧校园源码 智慧学校源码 智慧校园管理系统源码 小程序+电子班牌 982
Java智慧校园系统源码 智慧校园源码 智慧学校源码 智慧校园管理系统源码 小程序+电子班牌 984
致远OA权限 1675
发博客会有金币吗 1013