首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >微信扫码支付

微信扫码支付

作者头像
故久
发布2019-12-16 17:23:44
5.8K0
发布2019-12-16 17:23:44
举报
文章被收录于专栏:故久故久

一,需要申请公司的微信公众号,以及商户号。然后在商户号中关联微信公众APPID。在商户平台添加扫码支付功能。

二.根据微信支付扫码开发文档进行开发

https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_1

在application.yml封装属性

##微信公众号的appid
app.wx-pay-appId=xxxxxxxx
###微信公众号的appSecret
app.wx-pay-appSecret=xxxxxxxxx
##微信商户号
app.wx-pay-mchId=xxxx
##微信商户号apikey
app.wx-pay-apiKey=xxxxxx
###统一下单接口(微信文档中有)
app.wx-pay-ufdoderUrl=https://api.mch.weixin.qq.com/pay/unifiedorder
###通知验证签名的接口路径
app.wx-pay-notifyUrl=http://xxxx/api/wx/get
app.wx-pay-refundUrl=https://api.mch.weixin.qq.com/secapi/pay/refund

工具类 HttpUtil

/**
 * http工具类,负责发起post请求并获取的返回
 */
public class HttpUtil {

    private final static int CONNECT_TIMEOUT = 5000; // in milliseconds
    private final static String DEFAULT_ENCODING = "UTF-8";

    public static String postData(String urlStr, String data){
        return postData(urlStr, data, null);
    }

    public static String postData(String urlStr, String data, String contentType){
        BufferedReader reader = null;
        try {
            URL url = new URL(urlStr);
            URLConnection conn = url.openConnection();
            conn.setDoOutput(true);
            conn.setConnectTimeout(CONNECT_TIMEOUT);
            conn.setReadTimeout(CONNECT_TIMEOUT);
            if(contentType != null)
                conn.setRequestProperty("content-type", contentType);
            OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream(), DEFAULT_ENCODING);
            if(data == null)
                data = "";
            writer.write(data);
            writer.flush();
            writer.close();

            reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), DEFAULT_ENCODING));
            StringBuilder sb = new StringBuilder();
            String line = null;
            while ((line = reader.readLine()) != null) {
                sb.append(line);
                sb.append("\r\n");
            }
            return sb.toString();
        } catch (IOException e) {
            //logger.error("Error connecting to " + urlStr + ": " + e.getMessage());
        }finally {
            try {
                if (reader != null)
                    reader.close();
            } catch (IOException e) {
            }
        }
        return null;
    }

    //发送get请求方法
    public static String sendGet(String url, String param) {
        String result = "";
        BufferedReader in = null;
        try {
            String urlNameString = url + "?" + param;
            System.out.println(urlNameString);
            URL realUrl = new URL(urlNameString);
            // 打开和URL之间的连接
            URLConnection connection = realUrl.openConnection();
            // 设置通用的请求属性
            connection.setRequestProperty("accept", "*/*");
            connection.setRequestProperty("connection", "Keep-Alive");
            connection.setRequestProperty("user-agent",
                    "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            // 建立实际的连接
            connection.connect();
            // 获取所有响应头字段
            Map<String, List<String>> map = connection.getHeaderFields();
            // 遍历所有的响应头字段
            for (String key : map.keySet()) {
                System.out.println(key + "--->" + map.get(key));
            }
            // 定义 BufferedReader输入流来读取URL的响应
            in = new BufferedReader(new InputStreamReader(
                    connection.getInputStream()));
            String line;
            while ((line = in.readLine()) != null) {
                result += line;
            }
        } catch (Exception e) {
            System.out.println("发送GET请求出现异常!" + e);
            e.printStackTrace();
        }
        // 使用finally块来关闭输入流
        finally {
            try {
                if (in != null) {
                    in.close();
                }
            } catch (Exception e2) {
                e2.printStackTrace();
            }
        }
        return result;
    }


    /**
     * 获取用户实际ip
     * @param request
     * @return
     */
    public static String getIpAddr(HttpServletRequest request){
        String ipAddress = request.getHeader("X-Real-IP");
        if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("x-forwarded-for");
        }
//        String ipAddress = request.getHeader("x-forwarded-for");
        if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("Proxy-Client-IP");
        }
        if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("WL-Proxy-Client-IP");
        }
        if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getRemoteAddr();
            if(ipAddress.equals("127.0.0.1") || ipAddress.equals("0:0:0:0:0:0:0:1")){
                //根据网卡取本机配置的IP
                InetAddress inet=null;
                try {
                    inet = InetAddress.getLocalHost();
                } catch (UnknownHostException e) {
                    e.printStackTrace();
                }
                ipAddress= inet.getHostAddress();
            }
        }
        //对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
        if(ipAddress!=null && ipAddress.length()>15){ //"***.***.***.***".length() = 15
            if(ipAddress.indexOf(",")>0){
                ipAddress = ipAddress.substring(0,ipAddress.indexOf(","));
            }
        }
        return ipAddress;
    }

    public static String getIpAddress(HttpServletRequest request) throws IOException {
        // 获取请求主机IP地址,如果通过代理进来,则透过防火墙获取真实IP地址

        String ip = request.getHeader("X-Forwarded-For");

        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("Proxy-Client-IP");
            }
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("WL-Proxy-Client-IP");
            }
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("HTTP_CLIENT_IP");
            }
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("HTTP_X_FORWARDED_FOR");
            }
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getRemoteAddr();
            }
        } else if (ip.length() > 15) {
            String[] ips = ip.split(",");
            for (int index = 0; index < ips.length; index++) {
                String strIp = (String) ips[index];
                if (!("unknown".equalsIgnoreCase(strIp))) {
                    ip = strIp;
                    break;
                }
            }
        }
        return ip;
    }

    /**
     * 复杂条件下取得ip地址
     */
    public InetAddress getLocalHostLANAddress() throws Exception {
        try {
            InetAddress candidateAddress = null;
            // 遍历所有的网络接口
            for (Enumeration ifaces = NetworkInterface.getNetworkInterfaces(); ifaces.hasMoreElements(); ) {
                NetworkInterface iface = (NetworkInterface) ifaces.nextElement();
                // 在所有的接口下再遍历IP
                for (Enumeration inetAddrs = iface.getInetAddresses(); inetAddrs.hasMoreElements(); ) {
                    InetAddress inetAddr = (InetAddress) inetAddrs.nextElement();
                    if (!inetAddr.isLoopbackAddress()) {// 排除loopback类型地址
                        if (inetAddr.isSiteLocalAddress()) {
                            // 如果是site-local地址,就是它了
                            return inetAddr;
                        } else if (candidateAddress == null) {
                            // site-local类型的地址未被发现,先记录候选地址
                            candidateAddress = inetAddr;
                        }
                    }
                }
            }
            if (candidateAddress != null) {
                return candidateAddress;
            }
            // 如果没有发现 non-loopback地址.只能用最次选的方案
            InetAddress jdkSuppliedAddress = InetAddress.getLocalHost();
            return jdkSuppliedAddress;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

}

创建签名工具类 PayToolUtil

public class PayToolUtil {
    /**
     * 是否签名正确,规则是:按参数名称a-z排序,遇到空值的参数不参加签名。
     * @return boolean
     */
    public static boolean isTenpaySign(String characterEncoding, SortedMap<Object, Object> packageParams, String API_KEY) {
        StringBuffer sb = new StringBuffer();
        Set es = packageParams.entrySet();
        Iterator it = es.iterator();
        while(it.hasNext()) {
            Map.Entry entry = (Map.Entry)it.next();
            String k = (String)entry.getKey();
            String v = (String)entry.getValue();
            if(!"sign".equals(k) && null != v && !"".equals(v)) {
                sb.append(k + "=" + v + "&");
            }
        }

        sb.append("key=" + API_KEY);

        //算出摘要
        String mysign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toLowerCase();
        String tenpaySign = ((String)packageParams.get("sign")).toLowerCase();

        //System.out.println(tenpaySign + "    " + mysign);
        return tenpaySign.equals(mysign);
    }

    /**
     * @author
     * @date 2016-4-22
     * @Description:sign签名
     * @param characterEncoding
     *            编码格式
     * @return
     */
    public static String createSign(String characterEncoding, SortedMap<Object, Object> packageParams, String API_KEY) {
        StringBuffer sb = new StringBuffer();
        Set es = packageParams.entrySet();
        Iterator it = es.iterator();
        while (it.hasNext()) {
            Map.Entry entry = (Map.Entry) it.next();
            String k = (String) entry.getKey();
            String v = (String) entry.getValue();
            if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {
                sb.append(k + "=" + v + "&");
            }
        }
        sb.append("key=" + API_KEY);
        String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
        return sign;
    }

    /**
     * @author
     * @date 2016-4-22
     * @Description:将请求参数转换为xml格式的string
     * @param parameters
     *            请求参数
     * @return
     */
    public static String getRequestXml(SortedMap<Object, Object> parameters) {
        StringBuffer sb = new StringBuffer();
        sb.append("<xml>");
        Set es = parameters.entrySet();
        Iterator it = es.iterator();
        while (it.hasNext()) {
            Map.Entry entry = (Map.Entry) it.next();
            String k = (String) entry.getKey();
            String v = (String) entry.getValue();
            if ("attach".equalsIgnoreCase(k) || "body".equalsIgnoreCase(k) || "sign".equalsIgnoreCase(k)) {
                sb.append("<" + k + ">" + "<![CDATA[" + v + "]]></" + k + ">");
            } else {
                sb.append("<" + k + ">" + v + "</" + k + ">");
            }
        }
        sb.append("</xml>");
        return sb.toString();
    }

    /**
     * 取出一个指定长度大小的随机正整数.
     *
     * @param length
     *            int 设定所取出随机数的长度。length小于11
     * @return int 返回生成的随机数。
     */
    public static int buildRandom(int length) {
        int num = 1;
        double random = Math.random();
        if (random < 0.1) {
            random = random + 0.1;
        }
        for (int i = 0; i < length; i++) {
            num = num * 10;
        }
        return (int) ((random * num));
    }

    /**
     * 获取当前时间 yyyyMMddHHmmss
     *
     * @return String
     */
    public static String getCurrTime() {
        Date now = new Date();
        SimpleDateFormat outFormat = new SimpleDateFormat("yyyyMMddHHmmss");
        String s = outFormat.format(now);
        return s;
    }

    /**
     * 时间戳
     */

    public static long getTimeStamp() {
        Date d = new Date();
        long timeStamp = d.getTime() / 1000;     //getTime()得到的是微秒, 需要换算成秒
        return timeStamp;
    }


    public static Map<String, String> xmlToMap(String strXML) throws Exception {
        try {
            Map<String, String> data = new HashMap<String, String>();
            DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
            InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8"));
            org.w3c.dom.Document doc = documentBuilder.parse(stream);
            doc.getDocumentElement().normalize();
            NodeList nodeList = doc.getDocumentElement().getChildNodes();
            for (int idx = 0; idx < nodeList.getLength(); ++idx) {
                Node node = nodeList.item(idx);
                if (node.getNodeType() == Node.ELEMENT_NODE) {
                    org.w3c.dom.Element element = (org.w3c.dom.Element) node;
                    data.put(element.getNodeName(), element.getTextContent());
                }
            }
            try {
                stream.close();
            } catch (Exception ex) {
                // do nothing
            }
            return data;
        } catch (Exception ex) {
            throw ex;
        }

    }

}

XMLUtil4jdom类

public class XMLUtil4jdom {
    /**
     * 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。
     * @param strxml
     * @return
     */
    public static Map doXMLParse(String strxml) throws JDOMException, IOException {
        strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");

        if(null == strxml || "".equals(strxml)) {
            return null;
        }

        Map<String, String> m = new HashMap<String, String>();
        InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8"));
        SAXBuilder builder = new SAXBuilder();
        Document doc = builder.build(in);
        Element root = doc.getRootElement();
        List list = root.getChildren();
        Iterator it = list.iterator();
        while(it.hasNext()) {
            Element e = (Element) it.next();
            String k = e.getName();
            String v = "";
            List children = e.getChildren();
            if(children.isEmpty()) {
                v = e.getTextNormalize();
            } else {
                v = XMLUtil4jdom.getChildrenText(children);
            }

            m.put(k, v);
        }

        //关闭流
        in.close();

        return m;
    }

    /**
     * 获取子结点的xml
     * @param children
     * @return String
     */
    public static String getChildrenText(List children) {
        StringBuffer sb = new StringBuffer();
        if(!children.isEmpty()) {
            Iterator it = children.iterator();
            while(it.hasNext()) {
                Element e = (Element) it.next();
                String name = e.getName();
                String value = e.getTextNormalize();
                List list = e.getChildren();
                sb.append("<" + name + ">");
                if(!list.isEmpty()) {
                    sb.append(XMLUtil4jdom.getChildrenText(list));
                }
                sb.append(value);
                sb.append("</" + name + ">");
            }
        }

        return sb.toString();
    }
}

二维码工具类

public class QRUtil {

    public static String createQrCode(String url, String path, String fileName) {
        try {
            Map<EncodeHintType, String> hints = new HashMap<>();
            hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
            BitMatrix bitMatrix = new MultiFormatWriter().encode(url, BarcodeFormat.QR_CODE, 400, 400, hints);
            File file = new File(path, fileName);
            if (file.exists() || ((file.getParentFile().exists() || file.getParentFile().mkdirs()) && file.createNewFile())) {
                writeToFile(bitMatrix, "jpg", file);
                System.out.println("搞定:" + file);
                return file.toString();
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static void writeToFile(BitMatrix matrix, String format, File file) throws IOException {
        BufferedImage image = toBufferedImage(matrix);
        if (!ImageIO.write(image, format, file)) {
            throw new IOException("Could not write an image of format " + format + " to " + file);
        }
    }

    public static void writeToStream(BitMatrix matrix, String format, OutputStream stream) throws IOException {
        BufferedImage image = toBufferedImage(matrix);
        if (!ImageIO.write(image, format, stream)) {
            throw new IOException("Could not write an image of format " + format);
        }
    }

    private static final int BLACK = 0xFF000000;
    private static final int WHITE = 0xFFFFFFFF;

    private static BufferedImage toBufferedImage(BitMatrix matrix) {
        int width = matrix.getWidth();
        int height = matrix.getHeight();
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        for (int x = 0; x < width; x++) {
            for (int y = 0; y < height; y++) {
                image.setRGB(x, y, matrix.get(x, y) ? BLACK : WHITE);
            }
        }
        return image;
    }
    public static void main(String[] args) {
        createQrCode("www.baidu.com","D:\\","code.jpg");
    }
}

seviceImpl的类。统一下单

@Value("${app.wx-pay-appId}")
private String appid;

@Value("${app.wx-pay-appSecret}")
private String appsecret;

@Value("${app.wx-pay-mchId}")
private String mch_id;

@Value("${app.wx-pay-apiKey}")
private String key;

@Value("${app.wx-pay-ufdoderUrl}")
private String ufdoderUrl;

@Value("${app.wx-pay-notifyUrl}")
private String notify_url;

@Value("${app.wx-pay-refundUrl}")
private String refundUrl;    

      
      String requestId = UUID.randomUUID().toString().replace("-", "").toLowerCase();

       String out_trade_no =""+System.currentTimeMillis(); //订单号,先生成随机数。后修改为正式的

        String currTime = PayToolUtil.getCurrTime();
        String strTime = currTime.substring(8, currTime.length());
        String strRandom = PayToolUtil.buildRandom(4) + "";
        //随机字符串生成
        String nonce_str = strTime + strRandom;

       //交易类型
        String trade_type = "NATIVE";
        SortedMap<Object,Object> packageParams = new TreeMap<Object,Object>();
        packageParams.put("appid", appid);
        packageParams.put("mch_id", mch_id);
        packageParams.put("nonce_str", nonce_str);//时间戳
        //测试
        packageParams.put("body", "哈哈哈哈哈");
        packageParams.put("out_trade_no", out_trade_no);

        //价格的单位为分
        packageParams.put("total_fee", "1");
       //正式的处理現在一定是小數點兩位的
       // packageParams.put("total_fee", 正式的价格.replace(".",""));
        
        packageParams.put("spbill_create_ip", "127.0.0.1");
        //正式获取真实IP
        //packageParams.put("spbill_create_ip", HttpUtil.getIpAddress(request));
        packageParams.put("notify_url", notify_url);
        packageParams.put("trade_type", trade_type);


        //签名
        String sign = PayToolUtil.createSign("UTF-8", packageParams,key);
        packageParams.put("sign", sign);

        String requestXML = PayToolUtil.getRequestXml(packageParams);
        System.out.println(requestXML);
        logger.info("requestId:{},function:{},request:{},response:{}",requestId ,"微信产品参数",packageParams.toString(),requestXML);
        String resXml = HttpUtil.postData(ufdoderUrl, requestXML);
        logger.info("requestId:{},userId:{},function:{}",requestId ,"请求微信支付");
        logger.info("requestId:{},function:{},request:{},response:{}",requestId ,"微信请求支付url",requestXML,resXml);

        Map map = null;
        try {
            map = XMLUtil4jdom.doXMLParse(resXml);
        } catch (JDOMException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        String urlCode = (String) map.get("code_url");
        logger.info("requestId:{},function:{},response:{}",requestId(),"微信获取支付url",urlCode);

controller层

public void pay(HttpServletRequest request, HttpServletResponse response) {
  String requestId = UUID.randomUUID().toString().replace("-", "").toLowerCase();

    //此处执行付款
    try {
       //得到二维码链接
        String text = iserver.xxxx();
        logger.info("function:{},response:{}" "微信pc扫码支付", text);
        System.out.println(text);
        //根据url来生成生成二维码
        int width = 300;
        int height = 300;
        //二维码的图片格式
        String format = "jpg";
        Hashtable hints = new Hashtable();
        //内容所使用编码
        hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
        BitMatrix bitMatrix;
        bitMatrix = new MultiFormatWriter().encode(text, BarcodeFormat.QR_CODE, width, height, hints);
        QRUtil.writeToStream(bitMatrix, format, response.getOutputStream());
    } catch (WriterException e) {
        logger.info("requestId:{} err:{}", requestId, e);
        logger.error("requestId:{} err:{}", requestId, e);
    } catch (IOException e) {
        logger.info("requestId:{} err:{}", requestId, e);
        logger.error("requestId:{} err:{}", requestId, e);
    }
}

微信回调

//微信回调
@PostMapping(value = "/get")
@SkipUserAuth
public void get(HttpServletRequest request, HttpServletResponse response) throws IOException, ServiceException {
    //读取参数
    InputStream inputStream;
    StringBuffer sb = new StringBuffer();
    inputStream = request.getInputStream();
    String s;
    BufferedReader in = null;
    try {
        in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    }
    while ((s = in.readLine()) != null) {
        sb.append(s);
    }
    in.close();
    inputStream.close();

    //解析xml成map
    Map<String, String> m = new HashMap<String, String>();
    try {
        m = XMLUtil4jdom.doXMLParse(sb.toString());
    } catch (JDOMException e) {
        e.printStackTrace();
    }
    logger.info("requestId:{},function:{},response:{}" , requestId , "微信回调接口" , sb.toString());

    //过滤空 设置 TreeMap
    SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>();
    Iterator it = m.keySet().iterator();
    while (it.hasNext()) {
        String parameter = (String) it.next();
        String parameterValue = m.get(parameter);

        String v = "";
        if (null != parameterValue) {
            v = parameterValue.trim();
        }
        packageParams.put(parameter, v);
    }

    // 账号信息
    String key = apiKey;


    //判断签名是否正确
    if (PayToolUtil.isTenpaySign("UTF-8", packageParams, key)) {
        //------------------------------
        //处理业务开始
        //------------------------------
        String resXml = "";
        if ("SUCCESS".equals((String) packageParams.get("result_code"))) {
            // 这里是支付成功

            String mch_id = (String) packageParams.get("mch_id");
            String openid = (String) packageParams.get("openid");
            String is_subscribe = (String) packageParams.get("is_subscribe");
            String out_trade_no = (String) packageParams.get("out_trade_no");

            String total_fee = (String) packageParams.get("total_fee");

            //////////执行自己的业务逻辑////////////////
            //暂时使用最简单的业务逻辑来处理:只是将业务处理结果保存到session中
            //(根据自己的实际业务逻辑来调整,很多时候,我们会操作业务表,将返回成功的状态保留下来)
            request.getSession().setAttribute("_PAY_RESULT", "OK");
            System.out.println("支付成功");
            //通知微信.异步确认成功.必写.不然会一直通知后台.八次之后就认为交易失败了.
            resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
                    + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
            logger.info("requestId:{},function:{},response:{}" , requestId , "微信回调打印success信息" , resXml);
        } else {
            resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
                    + "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";
            logger.info("requestId:{},function:{},response:{}" , requestId , "微信回调报文为空" , resXml);
        }

        //------------------------------
        //处理业务完毕
        //------------------------------
        BufferedOutputStream out = new BufferedOutputStream(
                response.getOutputStream());
        out.write(resXml.getBytes());
        out.flush();
        out.close();
    } else {
        logger.info("requestId:{},function:{}" , requestId , "微信回调签名验证失败");
        System.out.println("通知签名验证失败");
    }

}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云支付
云支付(Cloud Pay,CPay)为您提供开放、可靠的聚合收款技术服务和商户管理功能。云支付支持刷卡支付、扫码支付、一码多付多种支付方式。服务商也可使用云支付提供的 SDK 和 HTTPS 接口,将云支付集成进自己的系统中,为商户提供的个性化解决方案。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档