.net core 美团闪购对接---授权

      .net 难道真的不行了吗?真无奈呀,上次做支付对接,官方示例没有C#语言案例,这次做美团和饿了吗对接也没有C#示例的影子,大多都是java和php示例。做支付的时候,可以在网上搜一些案例,也有开源的人家封装好的案例,做起来有样板容易很多。这次做美团接口对接。哎,让我无语了,C#案例的我一个都没搜到。在网上一根毛都找不到。突然没有案例,这让我这个程序小裁缝怎末办?没办法只能自己根据api文档慢慢调试了,还好做了好几年开发,老脸还是保住了,经过一番折腾,终于接通了。哈哈哈哈。。。

     要做美团接口对接,自己要注册一个开发者账号,不过要提交审核资质,注意:这个必须通过真正的企业去认证,过程中要上传营业执照的。这样审核才能通过。哎,真是搞不懂,你就不能给开发者留条路,设了这个门槛,要测试对接就没法做了。如果要测试只能审核通过才能申请一个测试门店。

     至于如何注册这里就不多说了,自己根据官方文档一步一步来就行了。注册好后,申请一个测试门店,通过后如下可以查看门店信息:

 

 

 

 

 

 

这里我们需要几个重要字段: 1,appid:应用Id  2app screte 应用密钥   3,三方门店ID

 有了这两个重要参数,接下来就是如何调用接口了,根据官方文档介绍,如果要调用接口,必须要获得ISV安全认证,也就是应用授权。如何授权,请看:https://open-shangou.meituan.com/home/guide/market/10686

 

 

 这个流程看起来很详细,但是操作起来就麻烦了,我大致说一下啊流程:

  OAuth认证要两种方式,一种是官方推荐使用的,需要两步才能获取access_token,一种是快捷方式,只需要一步就能获取access_token值。

   第一种方式:先调用https://waimaiopen.meituan.com/api/v1/oauth/authorize 获取Code值,然后在把Code值作为参数传入接口:https://waimaiopen.meituan.com/api/v1/oauth/token 获取acces_token值,后续调用其它接口都需要携带这个access_token值才能访问。

 

 

 

 

第二种方式:直接调用https://waimaiopen.meituan.com/api/v1/oauth/authorize 就可以获取access_token值。

 

 这两种方式唯一区别就是authorize 接口参数response_type的值不行同,如何response_type=  code 获取的就是code值,需要两部才能获取到access_token.  如果response_type=token 就会直接获取access_token值。不要以为这样就可以了,这个过程你还需要对传入的参数尽心加密,也就是生签,对应参数sig,这里的加密方式是用MD5 32位加密方式对url和参数一起进行加密。具体加密方式请看:(https://opendj.meituan.com/home/guide/market/10683 )注意这里坑很多,不要粗略的看,要仔细,参数拼接是按照参数字母拍寻拼接的,不是随意拼接参数。我这里举个例子。就以生成code为例:

   需要参数:

 

 注意这里面有坑,需要一个timestamp时间戳参数。而这里面并没有给。所以记得加上。

   第一步:拼接参数:注意:拼接的时候要按照参数字母排序,至于怎末排序,后续我会给方法。

  排序后后拼接参数:app_Id=123&app_poi_code=34223&response_type=code&timestamp=124324324

第二步:url+"?"+拼接后的参数+app secret(应用密钥)

    url+app_Id=123&app_poi_code=34223&response_type=code&timestamp=124324324+app secret(注意:这里直接把密钥值放在后面,不要加参数)

第三步:对整个url路径进行MD5加密;加密后获取sig值;

sig=md5(url+app_Id=123&app_poi_code=34223&response_type=code&timestamp=124324324+app secret)

第四步:在将sig作为参数放在url路径中:

 url+app_Id=123&app_poi_code=34223&response_type=code&timestamp=124324324&sig=sig值(注意:这里不要在app secret密钥了)

这里流程大概就是这样了。

下面直接上代码:

首先是Post和Get请求,已经封装好了,直接调用就行了。注意(美团开放接口用的https,所以请求的时候要忽略认证。代码我已经做了忽略认证了,可以直接用)

  public class HttpHelper
  {


    /// <summary>
    /// 
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="requestUrl"></param>
    /// <returns></returns>
    public async  static Task<T> DoGet<T>(string requestUrl, object paraModel) where T : new()
    {
      var res = new T();
      using (var httpClientHandler = new HttpClientHandler())
      {
        httpClientHandler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => { return true; };
        using (var client = new HttpClient(httpClientHandler))
        {

          string url = GetUrl(requestUrl, paraModel);
          client.BaseAddress = new Uri(url);
          var result = await client.GetStringAsync(url);//.GetAwaiter().GetResult();
          res = (T)JsonConvert.DeserializeObject<T>(result);
        }
      }
      return res;
    }
    public static async Task<T> DoPost<T>(string requestUrl, object paraModel, string accessToken = "") where T : new()
    {
      var res = new T();
      using (var httpClientHandler = new HttpClientHandler())
      {
        httpClientHandler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => { return true; };
        using (var client = new HttpClient(httpClientHandler))
        {
          try
          {
         
           
            string url = GetUrl(requestUrl, paraModel);
            HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post,url);
            request.Headers.Add("Accept", "application/x-www-form-urlencoded");
            //if (!string.IsNullOrEmpty(accessToken))
            //{
            //  string value =$"Bearer {accessToken}";
            //  request.Headers.Add("Authorization", value);

            //}
              HttpResponseMessage response = client.SendAsync(request).Result;
              var respStr = await response.Content.ReadAsStringAsync();
              res = respStr.ToObject<T>();

          }
          catch (Exception ex)
          {

            throw;
          }
        }
      }
      return res;
    }

   /// <summary>
    /// 拼接Url请求路径(生成sig签名)
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="url"></param>
    /// <param name="TModel"></param>
    /// <returns></returns>
    public static string GetUrl<T>(string url, T TModel)
    {
      Hashtable hash = new Hashtable();
      var timestamp = DateTimeOffset.Now.ToUnixTimeSeconds();
      hash.Add("app_id", ConfigHelper.appid);
      hash.Add("timestamp", timestamp);
      if (TModel != null)
      {
        //取得m的Type实例
        Type t = TModel.GetType();
        //获取所有属性 
        PropertyInfo[] properties = t.GetProperties();

        //取得类的属性名并获取属性值
        foreach (PropertyInfo s in t.GetProperties())
        {
          hash.Add(s.Name, TModel.GetType().GetProperty(s.Name).GetValue(TModel, null).ToString());
        }
      }
      ArrayList keys = new ArrayList(hash.Keys);
      keys.Sort(); //按字母顺序进行排序
      string strSig = "";
      foreach (string key in keys)
      {
        if (key != null && key != "" && key != "sign")
        {
          strSig += "&" + key + "=" + hash[key];
        }
      }
      strSig = strSig.TrimStart('&');
      string strUrl = url + "?" + strSig ;
      string createSig = MD5Encrypt.Get32MD5(strUrl + ConfigHelper.appsecret).ToLower();
      string requestUrl = strUrl + "&sig=" + createSig;
      return requestUrl;
    }
   
  }

model 用来反序化返回值:

 public class AuthorizeResult
  {
    public int status { get; set; }
    public string code { get; set; }
    public string state { get; set; }
    public string message { get; set; }
  }

 md5 32位加密:

  /// <summary>
    /// 此代码示例通过创建哈希字符串适用于任何 MD5 哈希函数 (在任何平台) 上创建 32 个字符的十六进制格式哈希字符串
    /// 官网案例改编
    /// </summary>
    /// <param name="source"></param>
    /// <returns></returns>
    public static string Get32MD5(string source)
    {
      using (MD5 md5Hash = MD5.Create())
      {
        byte[] data = md5Hash.ComputeHash(Encoding.UTF8.GetBytes(source));
        StringBuilder sBuilder = new StringBuilder();
        for (int i = 0; i < data.Length; i++)
        {
          sBuilder.Append(data[i].ToString("x2"));
        }

        string hash = sBuilder.ToString();
        return hash.ToUpper();
      }
    }

 

业务处理:

  /// <summary>
    /// 获取商家授权码code的接口  (Get)
    /// </summary>
    public async static Task<AuthorizeResult> Authorize()
    {
      var model = new
      {
        app_poi_code = ConfigHelper.GoodId,    //门店代码。
        response_type = "code"                //授权方式,此处response_type=code。
      };
      var result = await HttpHelper.DoGet<AuthorizeResult>(ConfigHelper.GetAuthorize, model);
      return result;
    }

调用:

 var result = OauthAPI.Authorize().Result;

 

 

 

这里已经成功了,之所以报这个问题,是因为我已经获取的授权码了,如果授权码不过期,或者自己不调用刷新接口刷新。调用用就会出现已经发放的问题。

有一个案例后续其实都一样的。我对接了一笑部分,基本都差不多,封装好后就是直接调用了。

 

 

 

 

 

 对接美团接口的过程中,我发现美团的官方api是真的乱。

 1,参数部分没有明确说明:比如每次请求必带有appid和timestamp,但是api接口没有给。

2,参数返回值的类型,根本不统一,很乱。

3,出现层级参数,好多都需要转换成json字符串传入,不能传入对象。

 

posted @ 2023-04-05 17:30  代码如风~~~  阅读(316)  评论(5编辑  收藏  举报