Alipay's face-to-face scan function

First, a sandbox environment is needed, specifically for developers. It provides you with a seller account and a buyer account. Specific own Baidu.
You need to download the sandbox version of Alipay, and then log in to the provided merchant account to operate. Because in the sandbox environment, the QR code given by Alipay can only be scanned in the sandbox version of Alipay.
Sandbox sellers:
https://openhome.alipay.com/platform/appDaily.htm?tab=account
sandbox required configuration file information:
https://openhome.alipay.com/platform/appDaily.htm?tab= info
Pay in person demo
https://docs.open.alipay.com/54/104506/
requires a sra2 key generator, Alipay has available to download:
https://openclub.alipay.com/read.php?tid=955&fid = 46
Alipay paid in person developer documentation
https://docs.open.alipay.com/194

Scenario : The user places an order, and then we will remove the Alipay interface in the background. Of course, the Alipay interface requires some parameters, such as the title, order number, total amount, item details, and so on. Yes, just take it and transform it. Our main thing is to drop the Alipay interface and let him return a json string to us, which contains a URL and generates a QR code. Then we use this toolkit to generate this QR code and save it to our image server. Then, we use the sandbox Alipay scan code that we just downloaded. Then, if the payment is successful, Alipay will call back the local callback function (intranet mapping) that we set before. There are the results returned by Alipay in the parameters, such as order number, payment status, payment time, etc., but these are some of the results Alipay gave us. Before that, we must verify whether this callback function is initiated by Alipay. This method is provided in the demo of the package, which returns a Boolean value. Then when it is true, our next operation is to verify the correctness of the parameters of the returned result and to change the status in the database. Finally, if the payment is successful, Alipay will return success, otherwise, Alipay will call back after a period of time, see the documentation for details. The general process is like this. I won't leave a message. . . .

controller

package com.mmall.controller.protal;

import com.alipay.api.AlipayApiException;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.demo.trade.config.Configs;
import com.google.common.collect.Maps;
import com.mmall.common.Const;
import com.mmall.common.ServerResponse;
import com.mmall.dao.OrderMapper;
import com.mmall.pojo.Order;
import com.mmall.pojo.User;
import com.mmall.service.IOrderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * Created by 敲代码的卡卡罗特
 * on 2018/3/18 14:08.
 */
@RequestMapping("/order/")
@Controller
@Slf4j
public class OrderController {
    @Autowired
    IOrderService iOrderService;
    @RequestMapping("pay.do")
    @ResponseBody
    public ServerResponse pay(HttpSession httpSession, Long orderNo, HttpServletRequest request){
        User user = (User)httpSession.getAttribute(Const.CURRENT_USER);
        if (user==null){
            return ServerResponse.createByError("请登录账号");
        }
        System.out.println("request.getContextPath():----"+request.getContextPath());
        System.out.println("request.getSession().getServletContext()-----"+request.getSession().getServletContext().getRealPath("upload"));
        String upload = request.getSession().getServletContext().getRealPath("upload");
        return  iOrderService.pay(orderNo,user.getId(),upload);
    }
    @RequestMapping("alipay_callback.do")
    @ResponseBody
    public Object alipayCallback(HttpServletRequest httpServlet){
        HashMap<String, String> map = Maps.newHashMap();
        Map<String, String[]> parameterMap = httpServlet.getParameterMap();
        Set<String> set = parameterMap.keySet();
        for (String s: set){
            String[] strings = parameterMap.get(s);
            String str="";
            for (int i=0;i<strings.length;i++){
                str=i==strings.length-1?str+strings[i]:str+strings[i]+",";
            }
            map.put(s,str);
        }
        log.info("支付宝回调:sign:{},trade_status:{},参数:{}",map.get("sign"),map.get("trade_status"),map.toString());

        //验签是不是支付宝发起的回调
        map.remove("sign_type");
        try {
            Configs.init("zfbinfo.properties");
            boolean bool = AlipaySignature.rsaCheckV2(map, Configs.getAlipayPublicKey(), "utf-8", Configs.getSignType());
            if (!bool){
                return ServerResponse.createByError("非法请求,再请求老子就报警了");
            }

        } catch (AlipayApiException e) {
            log.error("支付宝验证回调异常",e);
        }
        ServerResponse alipaycall = iOrderService.alipaycall(map);
        if (alipaycall.isSuccess()){
            return Const.AlipayCallback.RESPONSE_SUCCESS;
        }
        return Const.AlipayCallback.RESPONSE_FAILED;
    }


    @RequestMapping("queryOrderPayStatus.do")
    @ResponseBody
    public ServerResponse queryOrderPayStatus(HttpSession httpSession, Long orderNo){
        User user = (User)httpSession.getAttribute(Const.CURRENT_USER);
        if (user==null){
            return ServerResponse.createByError("请登录账号");
        }
        ServerResponse serverResponse = iOrderService.queryOrderPayStatus(user.getId(), orderNo);
        if (serverResponse.isSuccess()){
            return ServerResponse.createBySuccess(true);
        }
        return  ServerResponse.createBySuccess(false);
    }
}

service

package com.mmall.service.impl;

import com.alipay.api.AlipayResponse;
import com.alipay.api.response.AlipayTradePrecreateResponse;
import com.alipay.demo.trade.config.Configs;
import com.alipay.demo.trade.model.ExtendParams;
import com.alipay.demo.trade.model.GoodsDetail;
import com.alipay.demo.trade.model.builder.AlipayTradePrecreateRequestBuilder;
import com.alipay.demo.trade.model.result.AlipayF2FPrecreateResult;
import com.alipay.demo.trade.service.AlipayTradeService;
import com.alipay.demo.trade.service.impl.AlipayTradeServiceImpl;
import com.alipay.demo.trade.utils.ZxingUtils;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.mmall.common.Const;
import com.mmall.common.ServerResponse;
import com.mmall.dao.OrderItemMapper;
import com.mmall.dao.OrderMapper;
import com.mmall.dao.PayInfoMapper;
import com.mmall.pojo.Order;
import com.mmall.pojo.OrderItem;
import com.mmall.pojo.PayInfo;
import com.mmall.service.IOrderService;
import com.mmall.untis.BigDecimalUtil;
import com.mmall.untis.DateUtils;
import com.mmall.untis.FTPUtil;
import com.mmall.untis.PropertiesUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by 敲代码的卡卡罗特
 * on 2018/3/18 14:17.
 */
@Service
@Slf4j
public class OrderServiceImpl implements IOrderService {
    @Autowired
    OrderMapper orderMapper;
    @Autowired
    OrderItemMapper orderItemMapper;

    @Autowired
    PayInfoMapper payInfoMapper;
    // 支付宝当面付2.0服务
    private static AlipayTradeService tradeService;
    public ServerResponse pay(Long orderNo,Integer userId,String path){
        HashMap map = Maps.newHashMap();
        if (orderNo==null){
            return  ServerResponse.createByError("参数错误");
        }
        Order order = orderMapper.selectByUserIdAndOrderNo(userId, orderNo);
        if (order==null){
            return  ServerResponse.createByError("没有查到该订单");
        }
        map.put("orderNo",order.getOrderNo());
        // (必填) 商户网站订单系统中唯一订单号,64个字符以内,只能包含字母、数字、下划线,
        // 需保证商户系统端不能重复,建议通过数据库sequence生成,
        String outTradeNo = order.getOrderNo().toString();

        // (必填) 订单标题,粗略描述用户的支付目的。如“xxx品牌xxx门店当面付扫码消费”
        String subject = new StringBuilder().append("快乐慕商城订单:").append(outTradeNo).append("生成订单成功").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<GoodsDetail> goodsDetailList = new ArrayList<GoodsDetail>();
        List<OrderItem> orderItems = orderItemMapper.getOrderItemByUserIdAndOrderNo(userId, order.getOrderNo());
        for (OrderItem orderItem : orderItems){
            GoodsDetail goods1 = GoodsDetail.newInstance(orderItem.getProductId().toString(),
                    orderItem.getProductName(),
                    BigDecimalUtil.mul(orderItem.getCurrentUnitPrice().doubleValue(),orderItem.getQuantity()).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);
        /** 一定要在创建AlipayTradeService之前调用Configs.init()设置默认参数
         *  Configs会读取classpath下的zfbinfo.properties文件配置信息,如果找不到该文件则确认该文件是否在classpath目录
         */
        Configs.init("zfbinfo.properties");

        /** 使用Configs提供的默认参数
         *  AlipayTradeService可以使用单例或者为静态成员对象,不需要反复new
         */
        tradeService = new AlipayTradeServiceImpl.ClientBuilder().build();
        AlipayF2FPrecreateResult result = tradeService.tradePrecreate(builder);
        switch (result.getTradeStatus()) {
            case SUCCESS:
                log.info("支付宝预下单成功: )");

                AlipayTradePrecreateResponse response = result.getResponse();
                dumpResponse(response);
                File folder = new File(path);
                if (!folder.exists()){
                    folder.setWritable(true);
                    folder.mkdirs();
                }
                // 需要修改为运行机器上的路径
                String qrPath = String.format(path+"/qr-%s.png",
                        response.getOutTradeNo());
                String qrFileName = String.format("qr-%s.png",
                        response.getOutTradeNo());
                ZxingUtils.getQRCodeImge(response.getQrCode(),256,qrPath);
                File targetFile = new File(path, qrFileName);
                try {
                    FTPUtil.uploadFile(Lists.newArrayList(targetFile));
                } catch (IOException e) {
                    log.error("上传到ftp服务器的二维码异常");
                    e.printStackTrace();
                }
                log.info("filePath:" + qrPath);
                String qrurl= PropertiesUtil.getProperty("ftp.server.http.prefix")+targetFile.getName();
                map.put("qrurl",qrurl);
                return ServerResponse.createBySuccess(map);
            case FAILED:
                log.error("支付宝预下单失败!!!");
                return ServerResponse.createByError("支付宝预下单失败!!!");
            case UNKNOWN:
                log.error("系统异常,预下单状态未知!!!");
                return ServerResponse.createByError("系统异常,预下单状态未知!!!");
            default:
                log.error("不支持的交易状态,交易返回异常!!!");
                return ServerResponse.createByError("不支持的交易状态,交易返回异常!!!");
        }
    }

    // 简单打印应答
    private void dumpResponse(AlipayResponse response) {
        if (response != null) {
            log.info(String.format("code:%s, msg:%s", response.getCode(), response.getMsg()));
            if (StringUtils.isNotEmpty(response.getSubCode())) {
                log.info(String.format("subCode:%s, subMsg:%s", response.getSubCode(),
                        response.getSubMsg()));
            }
            log.info("body:" + response.getBody());
        }
    }


    public ServerResponse alipaycall(Map<String,String> map){
        Long out_trade_no = Long.valueOf(map.get("out_trade_no"));
        String trade_no = map.get("trade_no");
        String trade_status = map.get("trade_status");
        Order order = orderMapper.selectByOrderNo(out_trade_no);
        if (order==null){
            return ServerResponse.createByError("非快乐慕商城的订单,请忽略");
        }
        if (order.getStatus()>=Const.order.PAID.getCode()){
            return ServerResponse.createBySuccess("支付宝重复调用");
        }
        if (Const.AlipayCallback.TRADE_STATUS_TRADE_SUCCESS.equals(trade_status)){
            order.setStatus(Const.order.PAID.getCode());
            order.setPaymentTime(DateUtils.strToDate(map.get("gmt_create")));
            orderMapper.updateByPrimaryKey(order);
        }
        PayInfo payInfo = new PayInfo();
        payInfo.setUserId(order.getUserId());
        payInfo.setOrderNo(order.getOrderNo());
        payInfo.setPayPlatform(Const.PayPlatformEnum.ALI.getCode());
        payInfo.setPlatformNumber(trade_no);
        payInfo.setPlatformStatus(trade_status);

        payInfoMapper.insert(payInfo);
        return ServerResponse.createBySuccess();
    }

    public ServerResponse queryOrderPayStatus(Integer userId,Long orderNo){
        Order order = orderMapper.selectByUserIdAndOrderNo(userId, orderNo);
        if (order==null){
            return ServerResponse.createByError("没有该订单");
        }

        if (order.getStatus()>=Const.order.PAID.getCode()){
            return ServerResponse.createBySuccess(true);
        }
        return ServerResponse.createBySuccess(false);
    }
}
Published 281 original articles · 50 praises · 450,000 views +

Guess you like

Origin blog.csdn.net/lzh657083979/article/details/79599845