支付宝手机端H5授权登录和支付(asp.net core)

最近做一个项目,先后用到了微信和支付宝的授权登录和支付功能。 支付宝的流程相对简单一些,但是也不是很详细,网上找的说法都不太一致,走了很多坑。这里记录一下:

官网的.net demo还停留在.net framework 3.5时代。  好在有个大牛写了一个.net core版本的库 Alipay.AopSdk.AspnetCore,在github可以找到。接口和官方一致。

准备工作:

 要在开放平台注册app,添加授权登录,手机网页支付等功能。

找到AppID:

支付宝手机端H5授权登录和支付(asp.net core)_第1张图片

这个appid是每一个api都会用到的。

 

进入开发设置页面

支付宝手机端H5授权登录和支付(asp.net core)_第2张图片

 

授权回调地址:在授权登录获取用户信息时会用到,支付宝后台会把请求redirect到这里来,发送一个auth_code参数过来

  实际测试中,发现在程序中指定的回调地址可以和这里的不一致。这样就不用单独设置一个授权处理页面,每个页面都可被指定为回调地址,这样就可以减少一次redirect,会更快!

应用公钥:在我们发送信息给支付宝后台时需要用私钥签名,接收方用公钥接受,防止信息篡改。从支付宝官方下载的密钥生成器可以生成一对私钥和公钥。私钥存在应用的配置文件里。公钥在开发设置这里设进去。 

支付宝公钥:在支付宝后台发送信息给我们时支付宝会用它的私钥加签,我们用支付宝公钥解签。支付宝公钥是系统自动生成的。

 我们需要在应用中用到的是应用私钥,支付宝公钥。

AES密钥:是用来对发送的信息进行加密,防止其他人看到。这个在开发设置这里可以生成,我生成并保存了,但是在授权登录和支付过程中好像不需要。

appID和 应用私钥是最常用的两个参数,在本例中,它们被存储到  变量

CPubVars.g_zfbSetting.AppId, CPubVars.g_zfbSetting.PrivateKey 中

 

1.授权登录

 首先要转向这个网址:

string url = $"https://openauth.alipay.com/oauth2/publicAppAuthorize.htm?app_id={CPubVars.g_zfbSetting.AppId}&scope=auth_user&redirect_uri={redirectUri.UrlEncode()}&state=init";

 拼接的url里主要传入两个参数 {CPubVars.g_zfbSetting.AppId} 是支付宝应用的appid.

{redirectUri.UrlEncode()} 是转向验证的网址,支付宝后台会向这个网址get请求发送auth_code

注意:在说明文档中,redirectUri要在支付宝开放平台里指定好(授权回调地址),和这里的必须一致。但是实际测试,可以不一样

关于UrlEncode就是把一些特殊字符比如//等转换成其他字符。网上可以找到很多代码

在这个redirectUri网页里,获取request中的 auth_code,然后用它换取accessToken,再用accessToken获取用户信息

 AlipayUserInfoShareResponse userInfo =null;
                    DefaultAopClient client = null;
                    AlipaySystemOauthTokenResponse resAccessToken = null;
                    try
                    {
                        client = new DefaultAopClient("https://openapi.alipay.com/gateway.do", CPubVars.g_zfbSetting.AppId, CPubVars.g_zfbSetting.PrivateKey, false);
                        //client.encyptKey = "weP5E3wtp47VczFMB9Cq3Q==";
                       // client.alipayPublicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmM4+uk/1xVkVkhPzJtZomwMAdrzVrEntNgqmlN16sVL2r4nrZjSG0mjFBm69HcJT+yAWgqqxKeDucHT+HRy3KezJDkpOf5Dai5MklBFPl/TiT3LZ72L4nPtqTzytSSV7WDiHXM8oq0owrkTWwIHMFMG2eSNV11vElmohdsYvbPlgELDsHq6KSzp2azNO8c/ShYuXl/qOwp9ntkDfXzVJl0fG/7YkuEYNQt1tMDJS/G3WvPRLAVgqzsDus+Wo2+/NleuW9yMcjvo3l4TuuQJoO4HfllQkO8aGDLrsOyKj+V+n9s75C4TtR5CC7lhUsf0u2LTmlpI6PTBaPus/B42oMQIDAQAB";
                        var alipaySystemOauthTokenRequest = new AlipaySystemOauthTokenRequest
                        {
                            Code = auth_code,
                            GrantType = "authorization_code",
                            RefreshToken = ""
                        };
                        resAccessToken = client.Execute(alipaySystemOauthTokenRequest); 
                    }
                    catch (Exception ex)
                    {
                        return Content(ex.Message);
                    }
                    if (resAccessToken.IsError)
                    {
                        return Content("支付宝获取用户信息发生错误");
                    }
                    //HttpContext.Session.SetString("open_id", userInfo.UserId);
                    //open_id = userInfo.UserId;
                    try
                    {
                        var alipayUserUserinfoShareRequest = new AlipayUserInfoShareRequest();
                        userInfo = client.Execute(alipayUserUserinfoShareRequest, resAccessToken.AccessToken);
                      
                        open_id = m_UserInfo.openid=userInfo.UserId;
                        m_UserInfo.sex = userInfo.Gender=="F"?2:1;
                        m_UserInfo.nickname = userInfo.NickName==null?"未设置":userInfo.NickName;
                        m_UserInfo.province = userInfo.Province == null ? "" : userInfo.Province;
                        m_UserInfo.city = userInfo.City == null ? "" : userInfo.City;
                        m_UserInfo.headimgurl = userInfo.Avatar;
                        HttpContext.Session.SetString("sex", m_UserInfo.sex.ToString());
                        HttpContext.Session.SetString("nickname", m_UserInfo.nickname);
                        HttpContext.Session.SetString("province", m_UserInfo.province);
                        HttpContext.Session.SetString("city", m_UserInfo.city);
                        HttpContext.Session.SetString("headimgurl", m_UserInfo.headimgurl);
                        //if (m_UserInfo.unionid != null) HttpContext.Session.SetString("unionid", m_UserInfo.unionid);
                    }
                    catch (ErrorJsonResultException ex)
                    {
                        return Content(ex.Message);
                    }
                    var curUri=HttpContext.Session.GetString("curUri");
                    if (curUri != null)
                    {
                        if (!curUri.ToLower().Contains(".com/main"))
                        {
                            return Redirect(curUri);
                        } 
                    } 

获取完用户信息后,跳转到用户要访问的页面

2.手机网页支付

  在支付宝开放后台的app下找到手机网站支付功能,签约,需要上传营业执照支付宝手机端H5授权登录和支付(asp.net core)_第3张图片

 

在手机网页FillMoney中点击充值按钮后,js调用ajax,发起一个Post,在Post请求处理代码如下

  string sp_billno = string.Format("{0}{1}{2}", CPubVars.g_weixinSetting.TenPayV3_MchId/*10位*/, DateTime.Now.ToString("yyyyMMddHHmmss"),
                       TenPayV3Util.BuildRandomStr(6));
                DefaultAopClient client = new DefaultAopClient("https://openapi.alipay.com/gateway.do", CPubVars.g_zfbSetting.AppId, CPubVars.g_zfbSetting.PrivateKey, false);
                var alipayRequest = new AlipayTradeWapPayRequest();
                alipayRequest.SetReturnUrl("https://www.19liangpin.com/FillMoney/ZfbCallBack");
                alipayRequest.SetNotifyUrl("https://www.19liangpin.com/FillMoney/ZfbNotify");
                alipayRequest.BizContent = new
                {
                    out_trade_no = sp_billno,
                    total_amount = (int)(Convert.ToSingle(data.fill_amount) * 100),
                    subject = "订单说明",
                    seller_id = CPubVars.g_zfbSetting.Uid,
                    product_code = "QUICK_WAP_PAY"
                    //product_code = "FAST_INSTANT_TRADE_PAY";
                }.ToJson();
                var zfbForm = client.pageExecute(alipayRequest).Body;
                 
                zfbForm = zfbForm.Replace("", "");//.Replace("\"", "\\\"");
            
                return Json(new { result = "OK", zfbForm });

这两行是设置即时回调页面和支付结果通知页面。 alipayRequest.SetReturnUrl("https://www.19liangpin.com/FillMoney/ZfbCallBack");   //支付宝完毕会跳转到的页面Get
alipayRequest.SetNotifyUrl("https://www.19liangpin.com/FillMoney/ZfbNotify"); //支付结果通知页面(Post)

seller_id设置的是收款商户的商户ID。如果是自己收款用,就填写自己的商户号,如果你是支付服务商,可以填写其他商户ID.

填写其他商户ID要注意,你需要在支付宝开放平台的商家管理菜单下,为该商户签约指定服务(本例是WAP手机支付)之后才可以。否则会提示(该商户的支付宝账号暂不支持收款,请联系商户核实信息)。

支付宝手机端H5授权登录和支付(asp.net core)_第4张图片

签约过程和为自己的商户签约差不多,需要上传营业执照,指定网址,此外还需要一张店主委托书。在为商户签约前,该商户需要在手机支付宝里完善个人资料,上传身份证信息。否则在后台选择该商户时会提示该商户信息不完整。

 

 

最后返回的zfbForm是一个html form,这个form里的内容是支付宝后台自动生成的。js的ajax调用取到这个form后,附加到document的body上,然后submit即可:

 $.ajax({
            url: '/FillMoney/Fill',
            type: 'POST',
            contentType: 'application/json',
            data: JSON.stringify(data),
            success: function (data) {
                if (data.result === 'OK') {
                     
                        var form = $(data.zfbForm);
                        $(document.body).append(form);
                        document.forms['alipaysubmit'].submit();
                    
                }
            },

 document.forms['alipaysubmit'].submit(); 这句就会发起支付

同步回调处理(Get):

 public IDictionary GetRequestGet() 
        { 
            int i = 0; 
            IDictionary sArray = new Dictionary();
            NameValueCollection coll; 
            //Load Form variables into NameValueCollection variable. 
            coll =(NameValueCollection) Request.Query; 
            // Get names of all forms into a string array. 
            String[] requestItem = coll.AllKeys;  
            for (i = 0; i < requestItem.Length; i++) 
            {
                string q = "";
                object ov = Request.Query[requestItem[i]];
                if (ov != null) q = ov.ToString();
                sArray.Add(requestItem[i], q); 
            } 
            return sArray;

        }
        /// 
        /// 支付同步回调
        /// 
        [HttpGet]
        public IActionResult ZfbCallback()
        {
            /* 实际验证过程建议商户添加以下校验。
            1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号,
            2、判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额),
            3、校验通知中的seller_id(或者seller_email) 是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email)
            4、验证app_id是否为该商户本身。
            */
            IDictionary sArray = GetRequestGet();
            if (sArray.Count != 0)
            {
                
                bool flag = AlipaySignature.RSACheckV1(sArray, CPubVars.g_zfbSetting.AlipayPublicKey, CPubVars.g_zfbSetting.CharSet, CPubVars.g_zfbSetting.SignType, false);
                if (flag)
                {
                    Console.WriteLine($"同步验证通过,订单号:{sArray["out_trade_no"]}");
                    ViewData["PayResult"] = "同步验证通过";
                }
                else
                {
                    Console.WriteLine($"同步验证失败,订单号:{sArray["out_trade_no"]}");
                    ViewData["PayResult"] = "同步验证失败";
                }
            }
            return View();
        }

异步通知(Post)

  public IDictionary GetRequestPost() 
        { 
            int i = 0;

            IDictionary sArray = new Dictionary();
            NameValueCollection coll; 
            //Load Form variables into NameValueCollection variable.

            coll =(NameValueCollection) 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; 
        } 
        /// 
        /// 支付异步回调通知 需配置域名 因为是支付宝主动post请求这个action 所以要通过域名访问或者公网ip
        /// 
        [HttpPost]
        public async void ZfbNotify()
        {
            /* 实际验证过程建议商户添加以下校验。
            1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号,
            2、判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额),
            3、校验通知中的seller_id(或者seller_email) 是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email)
            4、验证app_id是否为该商户本身。
            */
            IDictionary sArray = GetRequestPost();
            if (sArray.Count != 0)
            {
                bool flag = AlipaySignature.RSACheckV1(sArray, CPubVars.g_zfbSetting.AlipayPublicKey, CPubVars.g_zfbSetting.CharSet, CPubVars.g_zfbSetting.SignType, false);
                if (flag)
                {
                    //交易状态
                    //判断该笔订单是否在商户网站中已经做过处理
                    //如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,并执行商户的业务程序
                    //请务必判断请求时的total_amount与通知时获取的total_fee为一致的
                    //如果有做过处理,不执行商户的业务程序

                    //注意:
                    //退款日期超过可退款期限后(如三个月可退款),支付宝系统发送该交易状态通知
                    Console.WriteLine(Request.Form["trade_status"]); 
                    await Response.WriteAsync("success");
                }
                else
                {
                    await Response.WriteAsync("fail");
                }
            }
        }

支付服务商

    支付

你可能感兴趣的:(开发感悟)