Ping++ 支付接口对接

一、请求charge对象

  1. package com.bra.modules.util.pingplusplus;  
  2.   
  3. import com.bra.common.utils.SystemPath;  
  4. import com.pingplusplus.Pingpp;  
  5. import org.springframework.stereotype.Service;  
  6.   
  7. import java.io.File;  
  8.   
  9. /** 
  10.  * Created by Afon on 16/4/26. 
  11.  */  
  12. @Service  
  13. public class PingPlusPlusService {  
  14.       
  15.     /** 
  16.      * Pingpp 管理平台对应的 API Key 
  17.      */  
  18.     private final static String apiKey = “”;  
  19.   
  20.   
  21.     /** 
  22.      * Pingpp 管理平台对应的应用 ID 
  23.      */  
  24.     private final static String appId = “”;  
  25.     /** 
  26.      * 你生成的私钥路径 
  27.      */  
  28.     private final static String privateKeyFilePath = File.separator+SystemPath.getClassPath()+“res”+ File.separator+“rsa_private_key.pem”;  
  29.   
  30.     public static String charge(String orderNo,int amount,String subject,String body,String channel,String clientIP){  
  31.   
  32.         // 设置 API Key  
  33.         Pingpp.apiKey = apiKey;  
  34.   
  35.         // 设置私钥路径,用于请求签名  
  36.         Pingpp.privateKeyPath = privateKeyFilePath;  
  37.         PingPlusCharge charge=new PingPlusCharge(appId);  
  38.         String chargeString=charge.createCharge(orderNo,amount,subject,body,channel,clientIP);  
  39.         return chargeString;  
  40.     }  
  41. }  
package com.bra.modules.util.pingplusplus;

import com.bra.common.utils.SystemPath;
import com.pingplusplus.Pingpp;
import org.springframework.stereotype.Service;

import java.io.File;

/**
 * Created by Afon on 16/4/26.
 */
@Service
public class PingPlusPlusService {

    /**
     * Pingpp 管理平台对应的 API Key
     */
    private final static String apiKey = "";


    /**
     * Pingpp 管理平台对应的应用 ID
     */
    private final static String appId = "";
    /**
     * 你生成的私钥路径
     */
    private final static String privateKeyFilePath = File.separator+SystemPath.getClassPath()+"res"+ File.separator+"rsa_private_key.pem";

    public static String charge(String orderNo,int amount,String subject,String body,String channel,String clientIP){

        // 设置 API Key
        Pingpp.apiKey = apiKey;

        // 设置私钥路径,用于请求签名
        Pingpp.privateKeyPath = privateKeyFilePath;
        PingPlusCharge charge=new PingPlusCharge(appId);
        String chargeString=charge.createCharge(orderNo,amount,subject,body,channel,clientIP);
        return chargeString;
    }
}
二、生成charge 对象

  1. package com.bra.modules.util.pingplusplus;  
  2.   
  3. import com.pingplusplus.exception.PingppException;  
  4. import com.pingplusplus.model.Charge;  
  5.   
  6. import java.util.Calendar;  
  7. import java.util.HashMap;  
  8. import java.util.Map;  
  9.   
  10. /** 
  11.  * Created by Afon on 16/4/26. 
  12.  */  
  13. public class PingPlusCharge {  
  14.   
  15.     private String appId;  
  16.   
  17.   
  18.     PingPlusCharge(String appId) {  
  19.         this.appId = appId;  
  20.     }  
  21.   
  22.     public String createCharge(String orderNo, int amount, String subject, String body, String channel, String clientIP) {  
  23.   
  24.         /** 
  25.          * 或者直接设置私钥内容 
  26.          Pingpp.privateKey = ”—–BEGIN RSA PRIVATE KEY—–\n” + 
  27.          ”… 私钥内容字符串 …\n” + 
  28.          ”—–END RSA PRIVATE KEY—–\n”; 
  29.          */  
  30.         Map<String, Object> chargeMap = new HashMap<String, Object>();  
  31.         chargeMap.put(”amount”, amount);  
  32.         chargeMap.put(”currency”“cny”);  
  33.         chargeMap.put(”subject”, subject);  
  34.         chargeMap.put(”body”, body);  
  35.         chargeMap.put(”order_no”, orderNo);  
  36.         chargeMap.put(”channel”, channel);  
  37.   
  38.         Calendar cal = Calendar.getInstance();  
  39.         cal.add(Calendar.MINUTE, 15);//15分钟失效  
  40.         long timestamp = cal.getTimeInMillis()/ 1000L;  
  41.         chargeMap.put(”time_expire”, timestamp);  
  42.   
  43.         chargeMap.put(”client_ip”, clientIP); // 客户端 ip 地址(ipv4)  
  44.         Map<String, String> app = new HashMap<String, String>();  
  45.         app.put(”id”, appId);  
  46.         chargeMap.put(”app”, app);  
  47.         String chargeString = null;  
  48.         try {  
  49.             //发起交易请求  
  50.             Charge charge = Charge.create(chargeMap);  
  51.             // 传到客户端请先转成字符串 .toString(), 调该方法,会自动转成正确的 JSON 字符串  
  52.             chargeString = charge.toString();  
  53.         } catch (PingppException e) {  
  54.             e.printStackTrace();  
  55.         }  
  56.         return chargeString;  
  57.     }  
  58. }   
package com.bra.modules.util.pingplusplus;

import com.pingplusplus.exception.PingppException;
import com.pingplusplus.model.Charge;

import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by Afon on 16/4/26.
 */
public class PingPlusCharge {

    private String appId;


    PingPlusCharge(String appId) {
        this.appId = appId;
    }

    public String createCharge(String orderNo, int amount, String subject, String body, String channel, String clientIP) {

        /**
         * 或者直接设置私钥内容
         Pingpp.privateKey = "-----BEGIN RSA PRIVATE KEY-----\n" +
         "... 私钥内容字符串 ...\n" +
         "-----END RSA PRIVATE KEY-----\n";
         */
        Map<String, Object> chargeMap = new HashMap<String, Object>();
        chargeMap.put("amount", amount);
        chargeMap.put("currency", "cny");
        chargeMap.put("subject", subject);
        chargeMap.put("body", body);
        chargeMap.put("order_no", orderNo);
        chargeMap.put("channel", channel);

        Calendar cal = Calendar.getInstance();
        cal.add(Calendar.MINUTE, 15);//15分钟失效
        long timestamp = cal.getTimeInMillis()/ 1000L;
        chargeMap.put("time_expire", timestamp);

        chargeMap.put("client_ip", clientIP); // 客户端 ip 地址(ipv4)
        Map<String, String> app = new HashMap<String, String>();
        app.put("id", appId);
        chargeMap.put("app", app);
        String chargeString = null;
        try {
            //发起交易请求
            Charge charge = Charge.create(chargeMap);
            // 传到客户端请先转成字符串 .toString(), 调该方法,会自动转成正确的 JSON 字符串
            chargeString = charge.toString();
        } catch (PingppException e) {
            e.printStackTrace();
        }
        return chargeString;
    }
} 

三、webhook

  1. @RequestMapping(value = “webhooks”)  
  2.    @ResponseBody  
  3.    public void webhooks ( HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException  {  
  4.        /*System.out.println(“ping++ webhooks”);*/  
  5.        request.setCharacterEncoding(”UTF8”);  
  6.        //获取头部所有信息  
  7.        Enumeration headerNames = request.getHeaderNames();  
  8.        String signature=null;  
  9.        while (headerNames.hasMoreElements()) {  
  10.            String key = (String) headerNames.nextElement();  
  11.            String value = request.getHeader(key);  
  12.            if(“x-pingplusplus-signature”.equals(key)){  
  13.                signature=value;  
  14.            }  
  15.        }  
  16.        /*System.out.println(“signature”+signature);*/  
  17.        // 获得 http body 内容  
  18.        StringBuffer eventJson=new StringBuffer();  
  19.       BufferedReader reader= null;  
  20.        try {  
  21.            reader = request.getReader();  
  22.            do{  
  23.                eventJson.append(reader.readLine());  
  24.            }while(reader.read()!=-1);  
  25.        } catch (IOException e) {  
  26.            e.printStackTrace();  
  27.        }  
  28.        reader.close();  
  29.        JSONObject event=JSON.parseObject(eventJson.toString());  
  30.        boolean verifyRS=false;  
  31.        try {  
  32.            PublicKey publicKey= WebhooksVerifyService.getPubKey();  
  33.          /*  System.out.println(publicKey);*/  
  34.            verifyRS=WebhooksVerifyService.verifyData(eventJson.toString(),signature,publicKey);  
  35.        } catch (Exception e) {  
  36.            e.printStackTrace();  
  37.        }  
  38.   
  39.        if(verifyRS) {  
  40.            /*System.out.println(“签名验证成功”);*/  
  41.            if (“charge.succeeded”.equals(event.get(“type”))) {  
  42.                JSONObject data = JSON.parseObject(event.get(”data”).toString());  
  43.                JSONObject object = JSON.parseObject(data.get(”object”).toString());  
  44.                String orderId = (String) object.get(”order_no”);  
  45.                /*System.out.println(“orderId:”+orderId);*/  
  46.                String channel = (String) object.get(”channel”);  
  47.                String payType = null;  
  48.                int amountFen = (int) object.get(“amount”);  
  49.                Double amountYuan = amountFen * 1.0 / 100;//ping++扣款,精确到分,而数据库精确到元  
  50.                Double weiXinInput = null;  
  51.                Double aliPayInput = null;  
  52.                Double bankCardInput = null;  
  53.   
  54.                if (“wx”.equals(channel)) {  
  55.                    payType = ”4”;//支付类型(1:储值卡,2:现金,3:银行卡,4:微信,5:支付宝,6:优惠券,7:打白条;8:多方式付款;9:微信个人,10:支付宝(个人))  
  56.                    weiXinInput = amountYuan;  
  57.                } else if (“alipay”.equals(channel)) {  
  58.                    payType = ”5”;  
  59.                    aliPayInput = amountYuan;  
  60.                } else if (“upacp”.equals(channel) || “upacp_wap”.equals(channel) || “upacp_pc”.equals(channel)) {  
  61.                    payType = ”3”;  
  62.                    bankCardInput = amountYuan;  
  63.                }  
  64.                Double couponInput;  
  65.                ReserveVenueCons order = reserveAppVenueConsService.get(orderId);  
  66.   
  67.                if (order != null) {  
  68.                    Double orderPrice = order.getShouldPrice();  
  69.                    couponInput = orderPrice - amountYuan;//订单金额-ping++扣款 等于优惠金额  
  70.                    Boolean bool = reserveAppVenueConsService.saveSettlement(order, payType, amountYuan,  
  71.                            0.0, bankCardInput, weiXinInput, aliPayInput, couponInput);  
  72.                    if (bool) {  
  73.                      /*  System.out.println(“订单结算成功”);*/  
  74.                        response.setStatus(200);  
  75.                        //return ”订单结算成功”;  
  76.                    } else {  
  77.                       /* System.out.println(“订单结算失败”);*/  
  78.                        //return ”订单结算失败”;  
  79.                        response.setStatus(500);  
  80.                    }  
  81.                } else {  
  82.                   /* System.out.println(“该订单不存在”);*/  
  83.                    //return ”该订单不存在”;  
  84.                    response.setStatus(500);  
  85.                }  
  86.            }  
  87.        }else{  
  88.            /*System.out.println(“签名验证失败”);*/  
  89.            //return ”签名验证失败”;  
  90.            response.setStatus(500);  
  91.        }  
  92.    }  
 @RequestMapping(value = "webhooks")
    @ResponseBody
    public void webhooks ( HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException  {
        /*System.out.println("ping++ webhooks");*/
        request.setCharacterEncoding("UTF8");
        //获取头部所有信息
        Enumeration headerNames = request.getHeaderNames();
        String signature=null;
        while (headerNames.hasMoreElements()) {
            String key = (String) headerNames.nextElement();
            String value = request.getHeader(key);
            if("x-pingplusplus-signature".equals(key)){
                signature=value;
            }
        }
        /*System.out.println("signature"+signature);*/
        // 获得 http body 内容
        StringBuffer eventJson=new StringBuffer();
       BufferedReader reader= null;
        try {
            reader = request.getReader();
            do{
                eventJson.append(reader.readLine());
            }while(reader.read()!=-1);
        } catch (IOException e) {
            e.printStackTrace();
        }
        reader.close();
        JSONObject event=JSON.parseObject(eventJson.toString());
        boolean verifyRS=false;
        try {
            PublicKey publicKey= WebhooksVerifyService.getPubKey();
          /*  System.out.println(publicKey);*/
            verifyRS=WebhooksVerifyService.verifyData(eventJson.toString(),signature,publicKey);
        } catch (Exception e) {
            e.printStackTrace();
        }

        if(verifyRS) {
            /*System.out.println("签名验证成功");*/
            if ("charge.succeeded".equals(event.get("type"))) {
                JSONObject data = JSON.parseObject(event.get("data").toString());
                JSONObject object = JSON.parseObject(data.get("object").toString());
                String orderId = (String) object.get("order_no");
                /*System.out.println("orderId:"+orderId);*/
                String channel = (String) object.get("channel");
                String payType = null;
                int amountFen = (int) object.get("amount");
                Double amountYuan = amountFen * 1.0 / 100;//ping++扣款,精确到分,而数据库精确到元
                Double weiXinInput = null;
                Double aliPayInput = null;
                Double bankCardInput = null;

                if ("wx".equals(channel)) {
                    payType = "4";//支付类型(1:储值卡,2:现金,3:银行卡,4:微信,5:支付宝,6:优惠券,7:打白条;8:多方式付款;9:微信个人,10:支付宝(个人))
                    weiXinInput = amountYuan;
                } else if ("alipay".equals(channel)) {
                    payType = "5";
                    aliPayInput = amountYuan;
                } else if ("upacp".equals(channel) || "upacp_wap".equals(channel) || "upacp_pc".equals(channel)) {
                    payType = "3";
                    bankCardInput = amountYuan;
                }
                Double couponInput;
                ReserveVenueCons order = reserveAppVenueConsService.get(orderId);

                if (order != null) {
                    Double orderPrice = order.getShouldPrice();
                    couponInput = orderPrice - amountYuan;//订单金额-ping++扣款 等于优惠金额
                    Boolean bool = reserveAppVenueConsService.saveSettlement(order, payType, amountYuan,
                            0.0, bankCardInput, weiXinInput, aliPayInput, couponInput);
                    if (bool) {
                      /*  System.out.println("订单结算成功");*/
                        response.setStatus(200);
                        //return "订单结算成功";
                    } else {
                       /* System.out.println("订单结算失败");*/
                        //return "订单结算失败";
                        response.setStatus(500);
                    }
                } else {
                   /* System.out.println("该订单不存在");*/
                    //return "该订单不存在";
                    response.setStatus(500);
                }
            }
        }else{
            /*System.out.println("签名验证失败");*/
            //return "签名验证失败";
            response.setStatus(500);
        }
    }
四、WebhooksVerifyService
  1. package com.bra.modules.util.pingplusplus;  
  2.   
  3. import com.bra.common.utils.SystemPath;  
  4. import org.apache.commons.codec.binary.Base64;  
  5.   
  6. import java.io.*;  
  7. import java.security.*;  
  8. import java.security.spec.X509EncodedKeySpec;  
  9.   
  10. /** 
  11.  * Created by sunkai on 15/5/19. webhooks 验证签名示例 
  12.  * 
  13.  * 该实例演示如何对 Ping++ webhooks 通知进行验证。 
  14.  * 验证是为了让开发者确认该通知来自 Ping++ ,防止恶意伪造通知。用户如果有别的验证机制,可以不进行验证签名。 
  15.  * 
  16.  * 验证签名需要 签名、公钥、验证信息,该实例采用文件存储方式进行演示。 
  17.  * 实际项目中,需要用户从异步通知的 HTTP header 中读取签名,从 HTTP body 中读取验证信息。公钥的存储方式也需要用户自行设定。 
  18.  * 
  19.  *  该实例仅供演示如何验证签名,请务必不要直接 copy 到实际项目中使用。 
  20.  * 
  21.  */  
  22. public class WebhooksVerifyService {  
  23.   
  24.     private static String pubKeyPath = File.separator+ SystemPath.getClassPath()+“res”+ File.separator+“pingpp_public_key.pem”;  
  25.     private static String eventPath = File.separator+SystemPath.getClassPath()+“res”+ File.separator+“webhooks_raw_post_data.json”;  
  26.     private static String signPath = File.separator+SystemPath.getClassPath()+“res”+ File.separator+“signature.txt”;  
  27.   
  28.     /** 
  29.      * 验证 webhooks 签名,仅供参考 
  30.      * @param args 
  31.      * @throws Exception 
  32.      */  
  33.     public static void main(String[] args) throws Exception {  
  34.         runDemos();  
  35.     }  
  36.   
  37.     public static void runDemos() throws Exception {  
  38.         // 该数据请从 request 中获取原始 POST 请求数据, 以下仅作为示例  
  39.         String webhooksRawPostData = getStringFromFile(eventPath);  
  40.         System.out.println(”——- POST 原始数据 ——-“);  
  41.         System.out.println(webhooksRawPostData);  
  42.         // 签名数据请从 request 的 header 中获取, key 为 X-Pingplusplus-Signature (请忽略大小写, 建议自己做格式化)  
  43.         String signature = getStringFromFile(signPath);  
  44.         System.out.println(”——- 签名 ——-“);  
  45.         System.out.println(signature);  
  46.         boolean result = verifyData(webhooksRawPostData, signature, getPubKey());  
  47.         System.out.println(”验签结果:” + (result ? “通过” : “失败”));  
  48.     }  
  49.   
  50.     /** 
  51.      * 读取文件, 部署 web 程序的时候, 签名和验签内容需要从 request 中获得 
  52.      * @param filePath 
  53.      * @return 
  54.      * @throws Exception 
  55.      */  
  56.     public static String getStringFromFile(String filePath) throws Exception {  
  57.         FileInputStream in = new FileInputStream(filePath);  
  58.         InputStreamReader inReader = new InputStreamReader(in, “UTF-8”);  
  59.         BufferedReader bf = new BufferedReader(inReader);  
  60.         StringBuilder sb = new StringBuilder();  
  61.         String line;  
  62.         do {  
  63.             line = bf.readLine();  
  64.             if (line != null) {  
  65.                 if (sb.length() != 0) {  
  66.                     sb.append(”\n”);  
  67.                 }  
  68.                 sb.append(line);  
  69.             }  
  70.         } while (line != null);  
  71.   
  72.         return sb.toString();  
  73.     }  
  74.   
  75.     /** 
  76.      * 获得公钥 
  77.      * @return 
  78.      * @throws Exception 
  79.      */  
  80.     public static PublicKey getPubKey() throws Exception {  
  81.         String pubKeyString = getStringFromFile(pubKeyPath);  
  82.         pubKeyString = pubKeyString.replaceAll(”(-+BEGIN PUBLIC KEY-+\\r?\\n|-+END PUBLIC KEY-+\\r?\\n?)”“”);  
  83.         byte[] keyBytes = Base64.decodeBase64(pubKeyString);  
  84.   
  85.         // generate public key  
  86.         X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);  
  87.         KeyFactory keyFactory = KeyFactory.getInstance(”RSA”);  
  88.         PublicKey publicKey = keyFactory.generatePublic(spec);  
  89.         return publicKey;  
  90.     }  
  91.   
  92.     /** 
  93.      * 验证签名 
  94.      * @param dataString 
  95.      * @param signatureString 
  96.      * @param publicKey 
  97.      * @return 
  98.      * @throws NoSuchAlgorithmException 
  99.      * @throws InvalidKeyException 
  100.      * @throws SignatureException 
  101.      */  
  102.     public static boolean verifyData(String dataString, String signatureString, PublicKey publicKey)  
  103.             throws NoSuchAlgorithmException, InvalidKeyException, SignatureException, UnsupportedEncodingException {  
  104.         byte[] signatureBytes = Base64.decodeBase64(signatureString);  
  105.         Signature signature = Signature.getInstance(”SHA256withRSA”);  
  106.         signature.initVerify(publicKey);  
  107.         signature.update(dataString.getBytes(”UTF-8”));  
  108.         return signature.verify(signatureBytes);  
  109.     }  
  110.   
  111. }  
package com.bra.modules.util.pingplusplus;

import com.bra.common.utils.SystemPath;
import org.apache.commons.codec.binary.Base64;

import java.io.*;
import java.security.*;
import java.security.spec.X509EncodedKeySpec;

/**
 * Created by sunkai on 15/5/19. webhooks 验证签名示例
 *
 * 该实例演示如何对 Ping++ webhooks 通知进行验证。
 * 验证是为了让开发者确认该通知来自 Ping++ ,防止恶意伪造通知。用户如果有别的验证机制,可以不进行验证签名。
 *
 * 验证签名需要 签名、公钥、验证信息,该实例采用文件存储方式进行演示。
 * 实际项目中,需要用户从异步通知的 HTTP header 中读取签名,从 HTTP body 中读取验证信息。公钥的存储方式也需要用户自行设定。
 *
 *  该实例仅供演示如何验证签名,请务必不要直接 copy 到实际项目中使用。
 *
 */
public class WebhooksVerifyService {

    private static String pubKeyPath = File.separator+ SystemPath.getClassPath()+"res"+ File.separator+"pingpp_public_key.pem";
    private static String eventPath = File.separator+SystemPath.getClassPath()+"res"+ File.separator+"webhooks_raw_post_data.json";
    private static String signPath = File.separator+SystemPath.getClassPath()+"res"+ File.separator+"signature.txt";

    /**
     * 验证 webhooks 签名,仅供参考
     * @param args
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {
        runDemos();
    }

    public static void runDemos() throws Exception {
        // 该数据请从 request 中获取原始 POST 请求数据, 以下仅作为示例
        String webhooksRawPostData = getStringFromFile(eventPath);
        System.out.println("------- POST 原始数据 -------");
        System.out.println(webhooksRawPostData);
        // 签名数据请从 request 的 header 中获取, key 为 X-Pingplusplus-Signature (请忽略大小写, 建议自己做格式化)
        String signature = getStringFromFile(signPath);
        System.out.println("------- 签名 -------");
        System.out.println(signature);
        boolean result = verifyData(webhooksRawPostData, signature, getPubKey());
        System.out.println("验签结果:" + (result ? "通过" : "失败"));
    }

    /**
     * 读取文件, 部署 web 程序的时候, 签名和验签内容需要从 request 中获得
     * @param filePath
     * @return
     * @throws Exception
     */
    public static String getStringFromFile(String filePath) throws Exception {
        FileInputStream in = new FileInputStream(filePath);
        InputStreamReader inReader = new InputStreamReader(in, "UTF-8");
        BufferedReader bf = new BufferedReader(inReader);
        StringBuilder sb = new StringBuilder();
        String line;
        do {
            line = bf.readLine();
            if (line != null) {
                if (sb.length() != 0) {
                    sb.append("\n");
                }
                sb.append(line);
            }
        } while (line != null);

        return sb.toString();
    }

    /**
     * 获得公钥
     * @return
     * @throws Exception
     */
    public static PublicKey getPubKey() throws Exception {
        String pubKeyString = getStringFromFile(pubKeyPath);
        pubKeyString = pubKeyString.replaceAll("(-+BEGIN PUBLIC KEY-+\\r?\\n|-+END PUBLIC KEY-+\\r?\\n?)", "");
        byte[] keyBytes = Base64.decodeBase64(pubKeyString);

        // generate public key
        X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PublicKey publicKey = keyFactory.generatePublic(spec);
        return publicKey;
    }

    /**
     * 验证签名
     * @param dataString
     * @param signatureString
     * @param publicKey
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeyException
     * @throws SignatureException
     */
    public static boolean verifyData(String dataString, String signatureString, PublicKey publicKey)
            throws NoSuchAlgorithmException, InvalidKeyException, SignatureException, UnsupportedEncodingException {
        byte[] signatureBytes = Base64.decodeBase64(signatureString);
        Signature signature = Signature.getInstance("SHA256withRSA");
        signature.initVerify(publicKey);
        signature.update(dataString.getBytes("UTF-8"));
        return signature.verify(signatureBytes);
    }

}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值