支付宝当面付扫码支付功能详解

来源:互联网 发布:php 下载文件 编辑:程序博客网 时间:2024/05/17 08:50

问题我怎么才能收到你们公众号平台的推送文章呢?

答案只需要点击标题下面的蓝色字【web前端开发】关注即可。


今天我们一起来讲解一下支付宝的支付功能,同样的,通过这一个例子,你就能使用支付宝其它的功能,还是那句老话,就当做是一个敲门砖吧,好了,下面就开始吧。

 

微信有测试公众号测试,那么支付宝呢?他也有,不过名字是叫做支付宝沙箱环境,地址:https://sandbox.alipaydev.com/sms/receive.htm ,扫码登陆之后:

>> APPID,支付宝网关,以及应用网关 这些呢是固定不变的。


>> 我们已经知道微信使用的是 SHA-1 加密,那么支付宝使用的就是RSA 和 RSA2 加密,当然了,支付宝推荐使用RSA2加密,两个的区别就是RSA2 是2048 位,RSA 是1024位,所以RSA2加密更好,我们就使用它就好。


>> 授权回调地址就是用户扫码进行支付之后,支付宝服务器回调我们程序接口的地址,规则呢跟微信的是一样的,这里不多讲了,不明白的请看一下上篇微信验证登录的讲解http://www.cnblogs.com/gudu1/p/8087130.html。


>> 接下来的就配置一下我们的密匙,使用哪个加密方式就配置哪个就好了,支付宝很周到,给我们提供了生成密匙的工具,下载位置:https://docs.open.alipay.com/291/105971 ,这个也是支付宝的官方文档,具体怎么使用,里面都很详细,这里就不占篇幅了,然后生成之后,就直接 copy 进去配置就好了,很简单的。

 

下面还是老样子,先贴出代码,然后讲解代码中的一些点,支付宝也提供了Java 、PHP、.NET  版本的支付代码,这点是非常到位的,所以我们只是站在巨人的肩膀上,前人栽树,后人乘凉,下载地址:https://docs.open.alipay.com/194/105201/ ,下载我们都会,下载之后把代码copy 出来,需要使用哪个功能就copy 哪个,因为我这里使用的支付宝生成二维码预下单的功能,然后:

Controller 


@Controller@RequestMapping("/order/")public class OrderController { @RequestMapping("pay.do")    @ResponseBody    private ServerResponse<Map<String, String>> pay(HttpSession session, Long orderNo, HttpServletRequest request) {        Integer userId = ((User) session.getAttribute(Const.CURRENT_USER)).getId();        String path = request.getSession().getServletContext().getRealPath(PropertiesUtil.getProperty("upload_image_path"));        if (orderNo == null || orderNo == 0L) {            return ServerResponse.createByErrorMessage("支付订单不能为空");        }        return orderService.pay(userId, orderNo, path);    }}


Service 的pay 方法:


@Service("iOrderService")public class OrderServiceImpl implements IOrderService {    private static final Logger logger = LoggerFactory.getLogger(OrderServiceImpl.class);    // 支付宝当面付2.0服务    private static AlipayTradeService tradeService;    static {        /** 一定要在创建AlipayTradeService之前调用Configs.init()设置默认参数         *  Configs会读取classpath下的zfbinfo.properties文件配置信息,如果找不到该文件则确认该文件是否在classpath目录         */        Configs.init("zfbinfo.properties");        /** 使用Configs提供的默认参数         *  AlipayTradeService可以使用单例或者为静态成员对象,不需要反复new         */        tradeService = new AlipayTradeServiceImpl.ClientBuilder().build();    }/**     * 支付宝预创建支付订单,生成支付的二维码用户用户扫码支付     *     * @param userId  用户ID     * @param orderNo 订单号     * @param path    本地上传图片地址     * @return     */    @Transactional    public ServerResponse pay(Integer userId, Long orderNo, String path) {        Map<String, String> mapResult = Maps.newHashMap();        Order order = orderMapper.selectOrderByOrderNo(orderNo);        if (order == null) {            return ServerResponse.createBySuccessMessage("该订单不存在");        }        // (必填) 商户网站订单系统中唯一订单号,64个字符以内,只能包含字母、数字、下划线,        // 需保证商户系统端不能重复,建议通过数据库sequence生成,        String outTradeNo = order.getOrderNo().toString();        // (必填) 订单标题,粗略描述用户的支付目的。如“xxx品牌xxx门店当面付扫码消费”        String subject = "Happy_mmall 扫码付款";        // (必填) 订单总金额,单位为元,不能超过1亿元        // 如果同时传入了【打折金额】,【不可打折金额】,【订单总金额】三者,则必须满足如下条件:【订单总金额】=【打折金额】+【不可打折金额】        String totalAmount = order.getPayment().toString();        // (可选) 订单不可打折金额,可以配合商家平台配置折扣活动,如果酒水不参与打折,则将对应金额填写至此字段        // 如果该值未传入,但传入了【订单总金额】,【打折金额】,则该值默认为【订单总金额】-【打折金额】        String undiscountableAmount = "0";        // 卖家支付宝账号ID,用于支持一个签约账号下支持打款到不同的收款账号,(打款到sellerId对应的支付宝账号)        // 如果该字段为空,则默认为与支付宝签约的商户的PID,也就是appid对应的PID        String sellerId = "";        // 订单描述,可以对交易或商品进行一个详细地描述,比如填写"购买商品2件共15.00元"        String body = new StringBuffer().append("订单:").append(order.getOrderNo()).append(",共花费").append(order.getPayment()).append("元").toString();        // 商户操作员编号,添加此参数可以为商户操作员做销售统计        String operatorId = "test_operator_id";        // (必填) 商户门店编号,通过门店号和商家后台可以配置精准到门店的折扣信息,详询支付宝技术支持        String storeId = "test_store_id";        // 业务扩展参数,目前可添加由支付宝分配的系统商编号(通过setSysServiceProviderId方法),详情请咨询支付宝技术支持        ExtendParams extendParams = new ExtendParams();        extendParams.setSysServiceProviderId("2088100200300400500");        // 支付超时,定义为120分钟        String timeoutExpress = "120m";        // 商品明细列表,需填写购买商品详细信息,        List<GoodsDetail> goodsDetailList = new ArrayList<GoodsDetail>();        // 添加 用户 预支付的订单中的商品        List<OrderItem> orderItemList = orderItemMapper.selectListByUserIdAndOrderNo(orderNo, userId);        for (OrderItem orderItem : orderItemList) {            GoodsDetail goods1 = GoodsDetail.newInstance(orderItem.getProductId().toString(), orderItem.getProductName(),                    BigDecimalUtil.mul(orderItem.getCurrentUnitPrice().doubleValue(), new Double(100)).longValue(),                    orderItem.getQuantity());            goodsDetailList.add(goods1);        }        // 创建扫码支付请求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.getProperty("alipay.callback.url"))//支付宝服务器主动通知商户服务器里指定的页面http路径,根据需要设置                .setGoodsDetailList(goodsDetailList);        // 创建预支付订单对象        AlipayF2FPrecreateResult result = tradeService.tradePrecreate(builder);        switch (result.getTradeStatus()) {            case SUCCESS:                logger.info("支付宝预下单成功: )");                // 获取响应 Response                AlipayTradePrecreateResponse response = result.getResponse();                //  简单打印一下日志                dumpResponse(response);                // 创建本地上传图片的文件夹,不存在则创建                File folder = new File(path);                if (!folder.exists()) {                    folder.setWritable(true);                    folder.mkdirs();                }                // 需要修改为运行机器上的路径,                String filePath = String.format(path + "/qr-%s.png",                        response.getOutTradeNo());                // %s 是一种占位符,即后面的response.getOutTradeNo() ,只是生成额随机字符串,防止重名                String fileName = String.format("/qr-%s.png", response.getOutTradeNo());                logger.info("filePath:" + filePath);                // 上传到本地服务器                ZxingUtils.getQRCodeImge(response.getQrCode(), 256, filePath);                File targetFile = new File(path, fileName);                try {                    // 上传图片到FTP服务器,上传FTP完毕之后,删除本地存储的图片                    FTPUtil.upload(Lists.newArrayList(targetFile));                } catch (IOException e) {                    logger.error("上传二维码失败", e);                    e.printStackTrace();                }                // 刚刚上传到FTP的图片地址URL                String qrPathUrl = PathUtil.getFTPImgPath(targetFile.getName());                mapResult.put("qrPath", qrPathUrl);                mapResult.put("orderNo", orderNo.toString());                return ServerResponse.createBySuccess(mapResult);            case FAILED:                logger.error("支付宝预下单失败!!!");                return ServerResponse.createByErrorMessage("支付宝预下单失败!!!");            case UNKNOWN:                logger.error("系统异常,预下单状态未知!!!");                return ServerResponse.createByErrorMessage("系统异常,预下单状态未知!!!");            default:                logger.error("不支持的交易状态,交易返回异常!!!");                return ServerResponse.createByErrorMessage("不支持的交易状态,交易返回异常!!!");        }    }}


 

>> 在此之前呢不要忘记添加支付宝的集成依赖jar包。


>> 为了方便理解,代码中的每一步都添加了注释,代码比较多,但是大多数的代码都是直接copy支付宝 提供的Demo,然后根据我们自己的业务需求修改。


>> 由于篇幅问题,请注意静态代码块中的代码,会加载一个配置文件,这个配置文件支付宝同样有提供,我们只要修改一下其中的参数值,APPID、PID(商户UID)、以及加密的公钥和私钥。


>> 我们程序支付整体的思路是这样:用户确认下单后,点击支付,然后会调用我们pay.do 这个接口,然后我们的程序在向支付宝服务器发送请求之前会添加一些参数,就是商品的订单号、收款平台信息、以及购买商品需要支付的总价格,需要修改的一处是 创建AlipayTradePrecreateRequestBuilder对象的时候,把call_back的URL修改成我们自己程序的接口,然后程序就向支付宝发送消息,因为这里支付宝集成做的特别好,只需要创建一个预支付对象,把需要的参数传进去,就可以发起预支付请求,返回二维码字节流,我们把二维码进行保存,然后展示给用户,进行扫码支付,用户扫码之后,不管是支付成功或者支付失败都会回调我们的call_back中配置的URL,进行处理。

 

 接下来就是我们的 call_back :


@Controller@RequestMapping("/order/")public class OrderController {    @RequestMapping("alipay_callback.do")    @ResponseBody    private ServerResponse callBack(HttpServletRequest request) throws AlipayApiException {        // 取出支付宝回调携带的所有参数并进行转换,数组转换为字符串        Map<String, String[]> tempParams = request.getParameterMap();        //  参数存放 Map        Map<String, String> requestParams = Maps.newHashMap();        for (Iterator<String> iterator = tempParams.keySet().iterator(); iterator.hasNext(); ) {            String key = iterator.next();            String[] strs = tempParams.get(key);            String str = "";            // 这里如果数组的长度是1,说明只有一个,直接赋值就好,如果超过一个,后面加一个逗号来隔离            for (int i = 0; i < strs.length; i++) {                str = strs.length - 1 == i ? str + strs[i] : str + strs[i] + ",";            }            requestParams.put(key, str);        }        // 去除sign_type        requestParams.remove("sign_type");        try {            // 验证签名            boolean result = AlipaySignature.rsaCheckV2(requestParams, Configs.getPublicKey(), "utf-8", Configs.getSignType());            if (!result) {                return ServerResponse.createByErrorMessage("非法请求,再恶意请求我就报警找网警了");            }        } catch (AlipayApiException e) {            logger.error("支付宝回调验证异常", e);            e.printStackTrace();            throw e;        }        // 调用Service 方法进行处理        ServerResponse serverResponse = orderService.alipayCallBack(requestParams);        if (!serverResponse.isSuccess()) {            logger.error("OrderController.callBack()","数据操作失败");            return ServerResponse.createBySuccess(Const.AlipayCallback.RESPONSE_FAILED);        }        logger.info("支付宝支付回调完成,没有异常");        return ServerResponse.createBySuccess(Const.AlipayCallback.RESPONSE_SUCCESS);    }}


 

>> 这个支付宝回调URL是这样:http://smyang.s1.natapp.cc/order/alipay_callback.do,对应我们的Controller 的RequestMapping中的路由,代码中都有添加注释,还是很清晰的。


>> 支付宝的验证签名的规则是怎样的呢?在通知返回的参数中除了sign_type和sign,其余的都是待验签的参数,详细请看 https://docs.open.alipay.com/194/103296/ ,但是这里只remove 掉了sign_type,通过查看源码发现,在支付宝集成代码中需要获取一下sign,然后它才remove 掉了sign,所以这里我们只需要remove掉sign_type就好,而且是必须的。


>> 在 rsaCheckV2() 方法中我们加入的参数sign_type,指定了使用哪种加密方式来验签,如果通过就确定这个支付过程是安全的,同时 Configs 这个类也是支付宝给我们提供的,很到位的吧!


>> 最后调用了Service方法进行我们的代码逻辑,比如:更新数据库中用户的支付状态,这个就是我们自己的业务需求了,在我们的业务代码中主要是通过支付宝回调参数中的 tradeStatus 这个字段来判断用户是否支付成功,具体的tradeStatus的状态,可以自行查看支付宝官方文档。

 

当然,支付宝不止这一种支付方式,另外还有好多,可以自行去查看,官方文档才是我们最好的老师。好了,到这里就先结束了,后续如果有不足的地方再另行修改。


文章来源:http://www.cnblogs.com/gudu1/p/8094197.html


关注我们


阅读全文
0 0
原创粉丝点击