码迷,mamicode.com
首页 > Web开发 > 详细

asp.net mvc 接入最新支付宝支付+退款 alipay-sdk-NET-20170615110549

时间:2017-08-01 14:32:32      阅读:235      评论:0      收藏:0      [点我收藏+]

标签:cms   font   下一步   数组   hang   local   写文本   status   根据   

第1步:

https://openhome.alipay.com/developmentDocument.htm

 技术分享

 第2步:下载sdkdemo

https://docs.open.alipay.com/270/106291/

 技术分享

 https://docs.open.alipay.com/54/103419

 技术分享

 3步:将SDK放到解决方案下并在解决方案下打开下载下来的SDK项目

 技术分享

4步:新建项目,项目中新建一个类存放支付宝配置相关信息

 技术分享

登录支付宝进入开发者中心

https://openhome.alipay.com/platform/appDaily.htm?tab=info

 技术分享

 

    public class AlipayConfigHelper

    {

        //↓↓↓↓↓↓↓↓↓↓请在这里配置您的基本信息↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

 

        //支付宝网关地址

        // -----开发环境地址-----

        //public static string serviceUrl = "https://openfile.alipay.com/chat/multimedia.do";

        // -----沙箱地址-----

        public static string serviceUrl = "https://openapi.alipaydev.com/gateway.do";

        // -----线上地址-----

        //public static string serviceUrl = "https://openapi.alipay.com/gateway.do";

 

        //应用ID,以2088开头由16位纯数字组成的字符串

        public static string appId = "2016080500169628";

        //开发者私钥,由开发者自己生成

        public static string privateKey = @"MIIEpAIBAAKCAQEA1g5VvHiQ3+1uQ2CkKbTOensvh4R4PfEEpGP/9fMsY6TnjGN7aFiJkHrEHFJVMy0mKyRzvLnTWPG6CXSAnzszOib6y6kScbqxaRUNFqpRzAshAQgOCxRISaVL1YZ5eVkWhQo9UMK5qD3NQd3pWqQVRj10bYXN41pMogEpLVs7fNk3q9g7Z9eIv/ZsS6z/AuTgXx6nNgcHZkHIZjL0smKEl+WGcS3EuXyOfAmc621ZF1Z0+9nu1MlnL9XelFNkxi5B4zXkeay9OyAsWA5nRFshXHNXZTWQHVRtjtfOWrbi4N4/Hx4pYDaIwWN4/bov5jRb7lp7rK6wNFJPW3QRGLj+qQIDAQABAoIBAEqtlZu7NE/rNZysCpnCSobQkzRkMs7pVwVDHflgbQwWa/G+gz9UJCoTqnd3AeJozfgfL4GYEvPk4VKLIpRqwuR21OhYg+bJiztt7LMLmJ3nBAyoEr4qxU1sLZjA4YBEZGblu56BUOQxK3rfQJouxH1M02GiXMIErteWMBdyzL4xJqots2cmsYO3TrXnKFF2GN2AJbMcgAg02VxGg4Lv7cZaczGKJ1jMS8iroKn6u87GdPNQgu5EnAluIt0+eu/jmZh+otdmwSXip0urk5QXkvJPemC+xQwhSSEDhQ1pHrL4303V3guHOywscUe+poc6mhghERCKQQu8VulsjE1J4UECgYEA718R88dokgluUbNLvR1pSLTtnF1qig5P/GRRQODcXaHdRoVVY19qVp606/FMsCr2/SO92maS7lBLNjUIDXk95v5mstfIFAbQBgT5lIOZLxGw2MCwQNF48Xakq0jx6+CSAfLYI5ZWtjbpLyJ6+c29ujXgfWz52sEIZphvA8quzBMCgYEA5O0PwcAHf9KPEDxphJ8Yloy17tzi2+FLiBksDO1urRI+yEvlt3z2we5ESNSO8vvBD6aaqpGZbLaVFvULpwn0XoU7c0+8nZjzYi2bjLXiSwmz138n6OG7W9KMpGcQP/UbIJ0T6GSY/BCSrk+DfEFLzQsd/PoSJrc249w9mRuhadMCgYEAgh9iOO0LkWeDCvK2l2VnmFiSWlAF3GqPXJqD+QrDgDq/eNh+ZHi368J2g1Q25gVVIqLNT+42rUoEuMD3Uau41NsypMHcR9a0OrF1oyAuo4GwawPETFgSsOBX/6y1pg38EQccK6xMEt2iwTazNxx6qMEk+cnFwP7yPCNdJeiGQPUCgYAGJA3OJraMyTspNlWmRxZDw5aFx8sWFt8N0Y8K8buMTPb+LHsHBwUfgqK2SW1kOzickN2JQcqZzfQF36DmZC/38R79IuBuYdH1ij+n8KEXXjVCzJhneNaryeQX7OPUs2B86XnFPB2WdWNZ5awjQyUjZO7zAEpIW5XPWvMUDlewHQKBgQCt15p1TyVYV2KFA92VdI5rT7v/xveQdtNm4BwW7clAXhB0vTl0Pf5TX75XZA1qim2RB6OcFI+RWv1gHO925Zt81NCODDYxA6P/R1OdOyPgAzsPvpo1/w/2oF1QS2q6gDVlZJXasPn4hzuaEVvzXePBJ6estPFwjWxPFVCyy5mUIQ==";

        //支付宝的公钥,由支付宝生成

        public static string alipayPublicKey = @"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxJo08UsEJPdsiWv0dRxhYEI2np5+VZSlkw4J/1SUauwGLrOvJD4duarnyixG2PzUFpgH/RR3EHO/clPWUSPA3pN4owlaKT5P6OYM8hQfVOuM992/lU2uSHh9ke3LzATwJVCwG27QUiQJGKBMk4Zzs0/Far/iqZUPLH/pE1NNpLGfpICGNavhyq7l22i6wMMpNo8Eq4tY8ef2ggJJh8QHCHEdULbDrvvos+z88oujcMZni5/kYDOrT9h1ypidRQLTlPL2HRWyspVkigvDtTr+aZizkTiYFSWa0DXUUK3nUjt14MOIFBwAvfLCBSZtFngwCNtRqjndyUlqgN+WPw6vVQIDAQAB";

 

        //服务器异步通知页面路径,需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问

        //public static string notify_url = "http://" + System.Web.HttpContext.Current.Request.Url.Host + ":" + System.Web.HttpContext.Current.Request.Url.Port + "/College/NotifyUrl";

        public static string notify_url = "http://ryan.wicp.net/College/NotifyUrl";

        //页面跳转同步通知页面路径,需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问

        //public static string return_url = "http://" + System.Web.HttpContext.Current.Request.Url.Host + ":" + System.Web.HttpContext.Current.Request.Url.Port + "/College/ReturnUrl";

        public static string return_url = "http://ryan.wicp.net/College/ReturnUrl";

        //参数返回格式,只支持json

        public static string format = "json";

        // 调用的接口版本,固定为:1.0

        public static string version = "1.0";

        // 商户生成签名字符串所使用的签名算法类型,目前支持RSA2和RSA,推荐使用RSA2

        public static string signType = "RSA2";

        // 字符编码格式 目前支持utf-8

        public static string charset = "utf-8";

        // false 表示不从文件加载密钥

        public static bool keyFromFile = false;

 

        //↑↑↑↑↑↑↑↑↑↑请在这里配置您的基本信息↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑

    }

5步:控制器处理

 技术分享

(1)初始化配置文件信息

        #region  初始化配置文件信息

        private IAopClient GetAlipayClient()

        {

            //支付宝网关地址         

            string serviceUrl = AlipayConfigHelper.serviceUrl;

 

            //应用ID,以2088开头由16位纯数字组成的字符串

            string appId = AlipayConfigHelper.appId;            

            //商户私钥

            string privateKey = AlipayConfigHelper.privateKey;

            //支付宝的公钥

            string alipayPublicKey = AlipayConfigHelper.alipayPublicKey;

 

            string format = AlipayConfigHelper.format;

            string version = AlipayConfigHelper.version;

            string signType = AlipayConfigHelper.signType;

            string charset = AlipayConfigHelper.charset;

            bool keyFromFile = false;

 

            IAopClient client = new DefaultAopClient(serviceUrl, appId, privateKey, format, version, signType, alipayPublicKey, charset, keyFromFile);

            return client;

        }

        #endregion

(2)调用SDK生成支付表单

        #region 调用SDK生成支付表单

        public ActionResult CreatePayForm()

        {

            try

            {

                IAopClient client = GetAlipayClient();

                AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();

 

                //在公共参数中设置回跳和通知地址

                request.SetReturnUrl(AlipayConfigHelper.return_url);

                request.SetNotifyUrl(AlipayConfigHelper.notify_url);

                var RRR=Request["orderNo"].ToString();

                //获取订单信息

                DataTable dr = collegeService.GetOrderInfo(RRR).Tables[0];

 

                //方法1:

                AlipayTradePayModel model = new AlipayTradePayModel();                

                //填充业务参数

                model.Body = "商学院报名";                    //订单描述

                model.Subject = "商学院报名";                 //订单标题

                model.OutTradeNo = Request["orderNo"];        //商户订单号,64个字符以内、可包含字母、数字、下划线;需保证在商户端不重复

                model.TotalAmount = (Convert.ToInt32(dr.Rows[0]["Amount"])*1.0/100).ToString();           //订单总金额,单位为元,精确到小数点后两位

                model.ProductCode = "FAST_INSTANT_TRADE_PAY"; //销售产品码,商家和支付宝签约的产品码 (如 FAST_INSTANT_TRADE_PAY)

                request.SetBizModel(model);

 

                //方法2:

                /**************************************************

                string Body = "商学院报名";

                string Subject = "商学院报名";

                string OutTradeNo = Request["orderNo"];

                string TotalAmount = Request["amount"];

                string ProductCode = "FAST_INSTANT_TRADE_PAY";

                request.BizContent = "{" +

                    "    \"body\":\""+ Body + "\"," +

                    "    \"subject\":\""+ Subject + "\"," +

                    "    \"out_trade_no\":\""+ OutTradeNo + "\"," +

                    "    \"total_amount\":"+ TotalAmount + "," +

                    "    \"product_code\":\""+ ProductCode + "\"" +

                    "  }";

                *********************************************************/

 

                //调用SDK生成表单

                AlipayTradePagePayResponse response = client.pageExecute(request);

                string form = response.Body;

                //Response.Write(form);

                return Content(form);

            }

            catch (Exception e)

            {

                throw new Exception(e.Message);

            }

        }

        #endregion

(3)面跳转同步通知页面

        #region 页面跳转同步通知页面

        /// <summary>

        /// 功能:页面跳转同步通知页面      

        /// </summary>

        public ActionResult ReturnUrl()

        {

            // http://localhost:10231/College/ReturnUrl?

            // total_amount =0.01

            // ×tamp=2017-07-24+17%3A57%3A08

            // &sign=********

            // &trade_no=2017072421001004930200313969

            // &sign_type=RSA2

            // &auth_app_id=2016080500169628

            // &charset=utf-8

            // &seller_id=2088102169996595

            // &method=alipay.trade.page.pay.return

            // &app_id=2016080500169628

            // &out_trade_no=GM201707241756580000000001

            // &version=1.0

 

            //将同步通知中收到的所有参数都存放到map中

            IDictionary<string, string> map = GetRequestGet();

 

            if (map.Count > 0) //判断是否有带返回参数

            {

                try

                {

                    //支付宝的公钥

                    string alipayPublicKey = AlipayConfigHelper.alipayPublicKey;

                    string signType = AlipayConfigHelper.signType;

                    string charset = AlipayConfigHelper.charset;

                    bool keyFromFile = false;

                    // 获取支付宝GET过来反馈信息  

                    bool verify_result = AlipaySignature.RSACheckV1(map, alipayPublicKey, charset, signType, keyFromFile);

                    if (verify_result)

                    {

                        // 验证成功                        

                        Response.Redirect("/College/Index?id=1");

                        return Content("ok");       

                    }

                    else

                    {

                        return Content("验证失败");

                    }

                }

                catch (Exception e)

                {

                    throw new Exception(e.Message);

                }

            }

            else

            {

                return Content("无返回参数");

            }

        }

        /// <summary>

        /// 获取支付宝GET过来通知消息,并以“参数名=参数值”的形式组成数组

        /// </summary>

        /// <returns>request回来的信息组成的数组</returns>

        public IDictionary<string, string> GetRequestGet()

        {

            int i = 0;

            IDictionary<string, string> sArray = new Dictionary<string, string>();

            NameValueCollection coll;

            //Load Form variables into NameValueCollection variable.

            coll = Request.QueryString;

 

            // Get names of all forms into a string array.

            String[] requestItem = coll.AllKeys;

 

            for (i = 0; i < requestItem.Length; i++)

            {

                sArray.Add(requestItem[i], Request.QueryString[requestItem[i]]);

            }

 

            return sArray;

        }

        #endregion

(4)服务器异步通知页面

        #region 服务器异步通知页面

        /// <summary>

        /// 功能:服务器异步通知页面

        /// 创建该页面文件时,请留心该页面文件中无任何HTML代码及空格。

        /// 该页面不能在本机电脑测试,请到服务器上做测试。请确保外部可以访问该页面。

        /// 该页面调试工具请使用写文本函数logResult。

        /// 如果没有收到该页面返回的 success 信息,支付宝会在24小时内按一定的时间策略重发通知

        /// net123    用户名:zouke1220  密码:zouke369189   邮箱:zouke12@163.com

        /// http://www.nat123.com/

        /// GmkCollege

        /// </summary>

        public void NotifyUrl()

        {

            // 获取支付宝Post过来反馈信息

            IDictionary<string, string> map = GetRequestPost();

 

            if (map.Count > 0) //判断是否有带返回参数

            {

                try

                {

                    //支付宝的公钥

                    string alipayPublicKey = AlipayConfigHelper.alipayPublicKey;

                    string signType = AlipayConfigHelper.signType;

                    string charset = AlipayConfigHelper.charset;

                    bool keyFromFile = false;

                     

                    bool verify_result = AlipaySignature.RSACheckV1(map, alipayPublicKey, charset, signType, keyFromFile);

                    // 验签成功后,按照支付结果异步通知中的描述,对支付结果中的业务内容进行二次校验,校验成功后在response中返回success并继续商户自身业务处理,校验失败返回failure

                    if (verify_result)

                    {

                        //商户订单号

                        string out_trade_no = map["out_trade_no"];

                        //支付宝交易号

                        string trade_no = map["trade_no"];

                        //交易创建时间

                        string gmt_create = map["gmt_create"];

                        //交易付款时间

                        string gmt_payment = map["gmt_payment"];

                        //通知时间

                        string notify_time = map["notify_time"];

                        //通知类型  trade_status_sync

                        string notify_type = map["notify_type"];

                        //通知校验ID

                        string notify_id = map["notify_id"];  

                        //开发者的app_id

                        string app_id = map["app_id"];

                        //卖家支付宝用户号

                        string seller_id = map["seller_id"];

                        //买家支付宝用户号

                        string buyer_id = map["buyer_id"];

                        //实收金额

                        string receipt_amount = map["receipt_amount"];  

                        //交易状态

                        //交易状态TRADE_FINISHED的通知触发条件是商户签约的产品不支持退款功能的前提下,买家付款成功;

                        //或者,商户签约的产品支持退款功能的前提下,交易已经成功并且已经超过可退款期限

                        //状态TRADE_SUCCESS的通知触发条件是商户签约的产品支持退款功能的前提下,买家付款成功

                        if (map["trade_status"] == "TRADE_FINISHED" || map["trade_status"] == "TRADE_SUCCESS")

                        {                 

                            //判断该笔订单是否在商户网站中已经做过处理

                            DataTable dd=collegeService.OrderPayNot(out_trade_no).Tables[0];

                            if (Convert.ToInt32(dd.Rows[0]["Status"]) == 0)

                            {

                                //如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,并执行商户的业务程序

                                #region 将数据提添加到集合中

                                Dictionary<string, string> myDic = new Dictionary<string, string>();

                                myDic.Add("PayTradeNo", trade_no);

                                myDic.Add("Status", "1");

                                myDic.Add("Type", "0");

                                myDic.Add("PayTime", gmt_payment);

                                myDic.Add("BuyerId", buyer_id);

                                myDic.Add("OrderNo", out_trade_no);

                                #endregion

 

                                #region 添加数据到数据库                

                                bool res = collegeService.AddPayInfo(myDic);

                                if (res == false)

                                {

                                    Response.Write("添加支付信息失败!");

                                }

                                #endregion

                                Response.Write("success");  //请不要修改或删除

                            }

                        }                        

                    }

                    // 验签失败则记录异常日志,并在response中返回failure.

                    else

                    {            

                        Response.Write("验证失败");

                    }

                }

                catch (Exception e)

                {

                    throw new Exception(e.Message);

                }

            }

            else

            {

                Response.Write("无返回参数");

            }

        }

        /// <summary>

        /// 获取支付宝POST过来通知消息,并以“参数名=参数值”的形式组成数组

        /// </summary>

        /// <returns>request回来的信息组成的数组</returns>

        public IDictionary<string, string> GetRequestPost()

        {

            int i = 0;

            IDictionary<string, string> sArray = new Dictionary<string, string>();

            NameValueCollection coll;

            //Load Form variables into NameValueCollection variable.

            coll = Request.Form;

 

            // Get names of all forms into a string array.

            String[] requestItem = coll.AllKeys;

 

            for (i = 0; i < requestItem.Length; i++)

            {

                sArray.Add(requestItem[i], Request.Form[requestItem[i]]);

            }

 

            return sArray;

        }

        #endregion

(5)退款

        #region 退款

        /// <summary>

        /// 支付宝退款

        /// </summary>

        /// <returns></returns>

        public string Refund(string OrderNo)

        {

            //查询要退款的订单信息

            DataTable dt = collegeService.GetOrderInfo(OrderNo).Tables[0];

            IAopClient client = GetAlipayClient();

            AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();

 

            AlipayTradePayModel model = new AlipayTradePayModel();

            //填充业务参数  

            request.BizContent = "{" +

            "    \"out_trade_no\":\"" + dt.Rows[0]["OrderNo"] + "\"," +

            "    \"trade_no\":\"" + dt.Rows[0]["TradeNo"] + "\"," +

            "    \"refund_amount\":" + ((int)dt.Rows[0]["Amount"] * 1.0 / 100) + "," +

            "    \"refund_reason\":\"正常退款\"," +

            "    \"operator_id\":\"OP001\"," +

            "    \"store_id\":\"NJ_S_001\"," +

            "    \"terminal_id\":\"NJ_T_001\"" +

            "  }";

 

            AlipayTradeRefundResponse response = client.Execute(request);

            string Info = response.Body;

            //{ "alipay_trade_refund_response":{

            //"code":"10000",

           //"msg":"Success",

           //"buyer_logon_id":"xso***@sandbox.com",

           //"buyer_user_id":"2088102172262939",

           //"fund_change":"Y",

           //"gmt_refund_pay":"2017-07-31 15:28:08",       

           //"open_id":"20881027348761637209827442915993",

           //"out_trade_no":"GM201707311527020000000001",

           //"refund_fee":"33.33",

           //"send_back_fee":"0.00",

           //"trade_no":"2017073121001004930200315623"},

           //"sign":"**********"}

            if (response.Code == "10000")

            {

                int RefundAmount =Convert.ToInt32(Convert.ToDecimal(response.RefundFee)*100);

                DateTime RefundTime =Convert.ToDateTime(response.GmtRefundPay);

                int Status = 2;   //已退

                bool res=collegeService.UpdateRefundInfo(OrderNo,RefundAmount, RefundTime, Status);

                return JsonHelper.DataJson(0, "退款成功!");

            }

            else

            {

                return JsonHelper.DataJson(1, "退款失败!");

            }

 

        }

        #endregion

第6步:新建支付订单确认页面

 技术分享 

<div class="zhezhaoceng dd" style="display: none">

    <div class="duihuakuang ee" style="display: none">

        <div class="success-alert ">

            <p>支付核实:</p>

        </div>

        <form method="post" id="OrderForm">

            <div>

                <input type="hidden" name="OrderNo" id="orderNo" value="" />

                <input type="hidden" name="Amount" id="amount" value="" />

                <div>

                    <p>商学院报名</p>

                    <p>订单号:<span id="OrderNo"></span></p>

                    <p>支付金额:<font color="red"><b><span id="Currency">¥</span><span id="Amount"></span></b></font></p>

                </div>

                <div>

                    <input class="submit next-btn" type="button" value="立即支付" onclick="go_pay()" />

                </div>

            </div>

        </form>

    </div>                   

</div>                                  

 

第7步:js处理

function go_pay() {

    $("#OrderForm").ajaxSubmit({

        url: "/College/CreatePayForm",

        type: "post",

        success: function (data) {

            $(".detail-message8").css({ "display": "none" });

            $(".dd").css({ "display": "none" });

            $(".ee").css({ "display": "none" });

            $(".pay_method").css({ "display": "none" });

            $("#pay_button").html(data);

        }

    });

}

8步:花生壳

由于支付宝异步回调测试需要将网站发布到公网,所以需要将本地域名映射到公网

(1)登录

http://hsk.oray.com/

用户名:************

 :*********

(2)实名认证

https://console.oray.com/

 技术分享

 (3)添加映射

https://b.oray.com/forward/

 技术分享

技术分享

技术分享

(4)访问将localhost换成zouke1220.oicp.net进行访问

http://zouke1220.oicp.net/College/Index

 技术分享

点击“报名”

 技术分享

点击“下一步”

function next_step() {

    $("#formID").ajaxSubmit({

        url: "/College/ValidateUserInfo",

        type: "post",

        success: function (data) {

            if (data == "ok") {

                $(".detail-message8").css({ "display": "none" });

                $(".detail-message6").css({ "display": "none" });

                $(".detail-message7").css({ "display": "block" });

            } else {

                alert(data);

                return false;

            }           

        }

    });

}

技术分享 

 

点击“同意”

function iAgree(){

    if ($("#checkedd").is(":checked")) {

        $(".detail-message6").css({ "display": "none" });

        $(".detail-message7").css({ "display": "none" });

        $(".detail-message8").css({ "display": "block" });

    } else {

        return false;

    }

}

 技术分享

技术分享

点击“去支付”跳到订单确认页

function order_confirm() {

    $("#SignUpForm").ajaxSubmit({

        url: "/College/SignUp",

        type: "post",

        success: function (data) {

            var obj=eval(‘(‘ + data + ‘)‘); //json转json对象

            if (obj.code == 0) {

                $(".dd").css({ "display": "block" });

                $(".ee").css({ "display": "block" });

 

                $("#OrderNo").html(obj.data.OrderNo);

                $("#Amount").html(obj.data.Amount);

 

                $("#orderNo").val(obj.data.OrderNo);

                $("#amount").val(obj.data.Amount);

            } else {

                alert(obj.msg);

                return false;

            }

        }

    });

}

 

 技术分享

点击“立即支付”

function go_pay() {

    $("#OrderForm").ajaxSubmit({

        url: "/College/CreatePayForm",

        type: "post",

        success: function (data) {

            $(".detail-message8").css({ "display": "none" });

            $(".dd").css({ "display": "none" });

            $(".ee").css({ "display": "none" });

            $(".pay_method").css({ "display": "none" });

            $("#pay_button").html(data);

        }

    });

}

 技术分享

点击“登录账户付款”

 技术分享

 点击“下一步”

 技术分享

点击“确认付款”

 技术分享

技术分享

自动跳转到异步回调页面,将支付成功信息插入数据库

http://zouke1220.oicp.net/College/NotifyUrl

并跳转到同步通知页面显示支付成功页面

http://zouke1220.oicp.net/College/ReturnUrl

 技术分享

 

第9步:所有测试通过后将第4步配置信息换成正式环境的

 

注:部分页面还需要美化,完整版项目参见:http://www.gmkcn.com/

asp.net mvc 接入最新支付宝支付+退款 alipay-sdk-NET-20170615110549

标签:cms   font   下一步   数组   hang   local   写文本   status   根据   

原文地址:http://www.cnblogs.com/zouke1220/p/7267923.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!