手撸的C#.net2.0环境下写的快手小店开放平台的SDK提供下载

这篇博客介绍了如何在C# .NET 2.0环境下手动实现快手小店开放平台的SDK,包括93个接口和121个类。示例展示了如何使用SDK获取订单列表的请求和响应实体类,并提供了调用接口的代码示例,帮助开发者在缺少对应SDK的情况下进行接口对接。
摘要由CSDN通过智能技术生成

由于近期要做快手小店开放平台的对接,获取快手小店的订单,商品等进行管理.所以就需要用到接口.但是快手小店开放平台open.kwaixiaodian.com只有java的sdk,我们开发的后端都是用的C#,而且是低版本.net环境.所以手撸了一套SDK.

包含目前的93个接口(完整)和121个类(完整)

手撸的C#.net2.0环境下写的快手小店开放平台的SDK提供下载-C#文档类资源-CSDN下载

一个下载订单的Request实体类

using System;
using System.Collections.Generic;
using System.Text;
using KuaishouSDK.Domain;
namespace KuaishouSDK.RequestAndResponse.订单API
{
	/// <summary>
	/// 获取订单列表(游标方式)
	/// 获取订单列表(游标方式)
	/// 授权:需要
	/// </summary>
	public class OpenSellerOrderPcursorListRequest : BaseKSRequest<OpenSellerOrderPcursorListResponse>
	{
		public override string GetApiName()
{
			return "open.seller.order.pcursor.list";
		}
		Int32 _type;
		/// <summary>
		/// 订单状态,0未知 1 全部 2 待付款 3 待发货 4 待收货(已发货)5 已收货 6 交易成功订单 7 已关闭订单
		/// 是否必须:是
		/// </summary>
		public Int32 type { get { return _type; } set { _type = value; } }

		Int64 _currentPage;
		/// <summary>
		/// 当前页
		/// 是否必须:是
		/// </summary>
		public Int64 currentPage { get { return _currentPage; } set { _currentPage = value; } }

		Int32 _pageSize;
		/// <summary>
		/// 每页请求数量  最多一页100条
		/// 是否必须:是
		/// </summary>
		public Int32 pageSize { get { return _pageSize; } set { _pageSize = value; } }

		Nullable<Int32> _sort;
		/// <summary>
		/// 1时间降序 2时间升序  默认降序
		/// 是否必须:否
		/// </summary>
		public Nullable<Int32> sort { get { return _sort; } set { _sort = value; } }

		Nullable<Int32> _queryType;
		/// <summary>
		/// 1按创建时间查找  2按更新时间查找  默认创建时间
		/// 是否必须:否
		/// </summary>
		public Nullable<Int32> queryType { get { return _queryType; } set { _queryType = value; } }

		Int64 _beginTime;
		/// <summary>
		/// 订单生成时间的开始时间,单位毫秒, 不能小于90天前,且需要小于结束时间
		/// 是否必须:是
		/// </summary>
		public Int64 beginTime { get { return _beginTime; } set { _beginTime = value; } }

		Int64 _endTime;
		/// <summary>
		/// 订单生成时间的截止时间,单位毫秒, 不能小于90天前,且与开始时间的时间范围不大于1天  (与开始时间的时间范围建议做成随时可配置,该范围可能在活动期间随时变化,比如变成小时级或者分钟级)。受春节活动影响,在2.1 10:00~2.5 10:00以及2.10 10:00~2.19 10:00期间订单的查询时间范围从1天缩短至1小时。
		/// 是否必须:是
		/// </summary>
		public Int64 endTime { get { return _endTime; } set { _endTime = value; } }

		Nullable<Int32> _cpsType;
		/// <summary>
		/// 分销类型 0-全部 1-普通订单 2-分销订单
		/// 是否必须:否
		/// </summary>
		public Nullable<Int32> cpsType { get { return _cpsType; } set { _cpsType = value; } }

		String _pcursor;
		/// <summary>
		/// 游标内容  第一次传空串,之后传上一次的pcursor返回值,若返回“nomore”则标识到底
		/// 是否必须:是
		/// </summary>
		public String pcursor { get { return _pcursor; } set { _pcursor = value; } }
	}
}

一个根据游标下载订单的Response实体类

using System;
using System.Collections.Generic;
using System.Text;
using KuaishouSDK.Domain;
namespace KuaishouSDK.RequestAndResponse.订单API
{
	/// <summary>
	/// 获取订单列表(游标方式)
	/// 获取订单列表(游标方式)
	/// 授权:需要
	/// </summary>
	public class OpenSellerOrderPcursorListResponse : KSResponse
	{
		Nullable<Int32> _result;
		/// <summary>
		/// 返回码
		/// </summary>
		Nullable<Int32> result { get { return _result; } set { _result = value; } }

		MerchantOrderListData _data;
		/// <summary>
		/// 订单列表
		/// </summary>
		public MerchantOrderListData data { get { return _data; } set { _data = value; } }

		String _error_msg;
		/// <summary>
		/// 错误码描述
		/// </summary>
		public String error_msg { get { return _error_msg; } set { _error_msg = value; } }
	}
}

部分数据类型的实体类

using System;
using System.Collections.Generic;
using System.Text;

namespace KuaishouSDK.Domain
{
	/// <summary>
	/// 订单列表
	/// </summary>
	public class MerchantOrderListData
	{
		Nullable<Int64> _currentPage;
		/// <summary>
		/// 当前页,仅第一次调用返回值有效
		/// </summary>
		public Nullable<Int64> currentPage { get { return _currentPage; } set { _currentPage = value; } }

		Nullable<Int32> _pageSize;
		/// <summary>
		/// 一页数据量
		/// </summary>
		public Nullable<Int32> pageSize { get { return _pageSize; } set { _pageSize = value; } }

		Nullable<Int64> _totalPage;
		/// <summary>
		/// 总页数,仅第一次调用返回值有效
		/// </summary>
		public Nullable<Int64> totalPage { get { return _totalPage; } set { _totalPage = value; } }

		Nullable<Int64> _totalSize;
		/// <summary>
		/// 总数仅第一次调用返回值有效
		/// </summary>
		public Nullable<Int64> totalSize { get { return _totalSize; } set { _totalSize = value; } }

		Nullable<Int64> _beginTime;
		/// <summary>
		/// 开始时间
		/// </summary>
		public Nullable<Int64> beginTime { get { return _beginTime; } set { _beginTime = value; } }

		Nullable<Int64> _endTime;
		/// <summary>
		/// 结束时间
		/// </summary>
		public Nullable<Int64> endTime { get { return _endTime; } set { _endTime = value; } }

		String _pcursor;
		/// <summary>
		/// 游标,nomore代表分页到底
		/// </summary>
		public String pcursor { get { return _pcursor; } set { _pcursor = value; } }

		List<MerchantOrderInfoView> _orderInfoList;
		/// <summary>
		/// 订单列表
		/// </summary>
		public List<MerchantOrderInfoView> orderInfoList { get { return _orderInfoList; } set { _orderInfoList = value; } }
	}
}


使用方法

 首先,

要通过快手小店开放平台的授权管理页面 找到授权店铺的授权code复制过来,然后通过拼接url的方式获取token

根据token发起请求.跟现在主流的Restful的API请求方式基本都一样.如下是token请求的获取连接.因为比较简单也基本不会变化,所以就没写到sdk里面做成一个方法.

https://open.kwaixiaodian.com/oauth2/access_token?app_id=你的appid&grant_type=code&app_secret=你的appsecret&code=复制来的授权码
            

调用Api的相关代码

OpenSellerOrderPcursorListRequest orderListGetReq = new OpenSellerOrderPcursorListRequest();
            orderListGetReq.type = 3;
            orderListGetReq.pcursor = "";
            orderListGetReq.pageSize = 100;
            orderListGetReq.currentPage = 1;
            
            DateTime end = DateTime.Now;
            DateTime start = end.AddDays(-7);

            orderListGetReq.beginTime = (long)(start - new DateTime(1970, 1, 1, 0, 0, 0)).TotalMilliseconds;
            orderListGetReq.endTime = (long)(end - new DateTime(1970, 1, 1, 0, 0, 0)).TotalMilliseconds;
            orderListGetReq.queryType = 1;
            orderListGetReq.cpsType = 0;

            OpenSellerOrderPcursorListResponse ordersListGetRsp = client.Execute(orderListGetReq, session);
            Console.ForegroundColor = ConsoleColor.Cyan;
            Console.WriteLine(ordersListGetRsp.Body);
            Console.ResetColor();

字段的所有的注释都是全的,但是重载了一下部分接口.比如字段名称为opreator的,改成了Opreator,因为C#中不支持变量名称为系统标识符,就好比你不能定义一个变量名字为int一样.


接口调用方法:

按照如下方式写个调用的测试函数或者是直接用到项目里面即可.

main函数演示如何创建sdk请求器对象,并且演示如何获取店铺信息,订单信息,服务订购情况等接口.

using KuaishouSDK;
using KuaishouSDK.RequestAndResponse.订单API;
using KuaishouSDK.RequestAndResponse.用户API;
using KuaishouSDK.RequestAndResponse.服务市场API;
using KuaishouSDK.RequestAndResponse.商品API;
using System;
using System.Collections.Generic;
using System.Text;

namespace 快手SDK测试CMD
{
    class Program
    {
        static void Main(string[] args)
        {
            DefaultKSClient client = new DefaultKSClient("https://open.kwaixiaodian.com", "你自己的appid写在这", "你自己的appsecret写在这", "你自己的signsecret写在这");
            string session = "通过拼接授权连接获取到的token写在这";
            OpenUserSellerGetRequest req = new OpenUserSellerGetRequest();
            OpenUserSellerGetResponse rsp = client.Execute(req, session);
            if (rsp.result>1)
            {
                Console.WriteLine("发生错误{0}", rsp.result);
                Console.ReadLine();
            }
            Console.WriteLine(string.Format("用户ID:{0}\r\nOpenId:{1}\r\n头像地址:{2}", rsp.data.sellerId,rsp.data.openId, rsp.data.head));
            //Console.ReadLine();

            OpenSellerOrderPcursorListRequest orderListGetReq = new OpenSellerOrderPcursorListRequest();
            orderListGetReq.type = 3;
            orderListGetReq.pcursor = "";
            orderListGetReq.pageSize = 100;
            orderListGetReq.currentPage = 1;
            
            DateTime end = DateTime.Now;
            DateTime start = end.AddDays(-7);

            orderListGetReq.beginTime = (long)(start - new DateTime(1970, 1, 1, 0, 0, 0)).TotalMilliseconds;
            orderListGetReq.endTime = (long)(end - new DateTime(1970, 1, 1, 0, 0, 0)).TotalMilliseconds;
            orderListGetReq.queryType = 1;
            orderListGetReq.cpsType = 0;

            OpenSellerOrderPcursorListResponse ordersListGetRsp = client.Execute(orderListGetReq, session);
            Console.ForegroundColor = ConsoleColor.Cyan;
            Console.WriteLine(ordersListGetRsp.Body);
            Console.ResetColor();



            OpenServiceMarketBuyerServiceInfoRequest breq = new OpenServiceMarketBuyerServiceInfoRequest();
            breq.buyerOpenId = rsp.data.openId;

            OpenServiceMarketBuyerServiceInfoResponse brsp = client.Execute(breq, session);
            Console.ForegroundColor = ConsoleColor.Magenta;
            Console.WriteLine(brsp.Body);
            Console.ResetColor();


            OpenServiceMarketOrderListRequest olreq = new OpenServiceMarketOrderListRequest();
            olreq.endTime = (long)(DateTime.Now - new DateTime(1970, 1, 1, 0, 0, 0)).TotalMilliseconds;
            olreq.pageNum = 1;
            olreq.pageSize = 100;
            olreq.queryType = 1;
            olreq.startTime = olreq.endTime - (30L * 24 * 60 * 60 * 1000);
            OpenServiceMarketOrderListResponse olrsp = client.Execute(olreq, session);

            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.WriteLine(olrsp.Body);
            Console.ResetColor();


            OpenItemListGetRequest itemsGetReq = new OpenItemListGetRequest();
            itemsGetReq.pageNumber = 1;
            itemsGetReq.pageSize = 20;
            OpenItemListGetResponse itemsGetRsp = client.Execute(itemsGetReq, session);

            Console.ForegroundColor = ConsoleColor.DarkBlue;
            Console.WriteLine(itemsGetRsp.Body);
            Console.ResetColor();

            Console.ReadLine();
        }
    }
}

2023年01月10日21:18:29补充一下

DefaultKSClient类的内容:

using Norman;
using Norman.FastJSON;
using KuaishouSDK;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;

namespace KuaishouSDK
{
    public class DefaultKSClient
    {
        #region 全局变量
        /// <summary>
        /// 遇到错误的时候的最大尝试次数,在应用未发布触发限流的时候也可使用此参数.如果应用限流,第一次调用会发生71005
        /// 之类的71开头的错误,这样的错误需要重试一下
        /// </summary>
        const int maxReTryTime = 20;
        #endregion
        string url, client_id, client_secret, sign_secret, date_type;
        public DefaultKSClient(string url, string client_id, string client_secret, string sign_secret, string date_type = "json")
        {
            this.url = url;
            this.client_id = client_id;
            this.client_secret = client_secret;
            this.sign_secret = sign_secret;
            this.date_type = date_type;
        }
        public T Execute<T>(IKSRequest<T> request) where T : KSResponse
        {
            return this.Execute(request, null, DateTime.Now);
        }
        public T Execute<T>(IKSRequest<T> request, string session) where T : KSResponse
        {
            return this.Execute(request, session, DateTime.Now);
        }
        /// <summary>
        /// 执行拼多多请求 2021年7月3日12:13:59  如果遇到限流错误自动重新执行一次,最多执行5次
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="request"></param>
        /// <param name="session"></param>
        /// <param name="timestamp"></param>
        /// <returns></returns>
        public T Execute<T>(IKSRequest<T> request, string session, DateTime timestamp) where T : KSResponse
        {
            T ret = null;
            int currentTime = 0;
            while (currentTime < maxReTryTime)
            {
                ret = DoExecute<T>(request, session, timestamp);
                if (ret != null && ret.IsError == false)
                {
                    break;
                }
                else if (ret.ErrCode > 0 && ret.ErrCode < 70000)
                {
                    Console.ForegroundColor = ConsoleColor.Red;
                    Console.WriteLine("快手SDK捕获到API错误:{0},终止重试", ret == null ? "response非实体" : ret.Body);
                    Console.ResetColor();
                    break;
                }
                else if (ret.ErrCode > 70000)
                {
                    Console.ForegroundColor = ConsoleColor.DarkRed;
                    Console.WriteLine("拼多多SDK捕获到应用限流错误:{0}", ret.ErrMsg);
                    Console.ResetColor();
                }
                currentTime++;
                System.Threading.Thread.Sleep(111);
            }
            return ret;
        }

        private T DoExecute<T>(IKSRequest<T> request, string session, DateTime timestamp) where T : KSResponse
        {
            Type responseType = typeof(T);
            T response = Activator.CreateInstance(responseType) as T;
            #region 组装请求参数
            WebUtils wu = new WebUtils();
            Dictionary<string, string> param4UrlBuild = new Dictionary<string, string>();
            Dictionary<string, string> waitSignParams = new Dictionary<string, string>();
            Dictionary<string, string> requestParams = new Dictionary<string, string>();
            #region 添加APPKEY
            waitSignParams.Add("appkey", this.client_id);
            #endregion
            #region 添加时间戳
            long timeStamp = (timestamp.ToUniversalTime().Ticks - 621355968000000000) / 10000;
            timeStamp = 1628490455275;
            if (waitSignParams.ContainsKey("timestamp") == false)
            {
                waitSignParams.Add("timestamp", timeStamp.ToString());
            }
            else
            {
                waitSignParams["timestamp"] = timeStamp.ToString();
            }
            #endregion
            #region 添加session
            if (session != null)
            {
                waitSignParams.Add("access_token", session);
            }
            #endregion
            #region 添加请求的API版本号,目前版本为1
            waitSignParams.Add("version", "1");
            #endregion

            #region 添加param
            #region 使用反射,获取request中的所有属性的值,添加到param
            Type requestType = request.GetType();
            PropertyInfo[] properties = requestType.GetProperties();

            if (properties == null)
            {
            }
            else
            {
                foreach (PropertyInfo p in properties)
                {
                    if (!requestParams.ContainsKey(p.Name))
                    {
                        object val = p.GetValue(request, null);
                        if (val != null)
                        {
                            string valStr = string.Format("{0}", val);
                            requestParams.Add(p.Name, valStr);
                        }
                    }
                    else
                    {
                        response.ErrMsg = string.Format("重复的参数信息:{0}", p.Name);
                        return response;
                    }
                }
            }
            #endregion
            string paramStr = JSON.ToJSON(requestParams, JSON.SimpllySettingParameters);
            waitSignParams.Add("param", paramStr);
            //if (requestParams.Count>0)
            //{
            //    string paramStr = JSON.ToJSON(requestParams, JSON.SimpllySettingParameters);
            //    waitSignParams.Add("param", paramStr);
            //}
            //else
            //{
            //    waitSignParams.Add("param", "");
            //}
            #endregion
            #region 添加api名字
            string apiName = request.GetApiName();
            if (apiName == null || apiName.Length < 1)
            {
                response.ErrMsg = "无效的api,名字不能为空";
                return response;
            }
            waitSignParams.Add("method", apiName);
            #endregion

            #region 添加签名算法 2021年8月9日14:33:49  这一步比较坑 解决了一个多小时
            /*
             * 最后找文档上的 支持中心->API测试工具,测试了一下用户API,看一下sign跟他实际调用的时候是不是一个
             * 但是突然想到 是不是要先把参数中   先  加入signMethod然后再去签名. 结果真的是这样的. waitSignParam里面要先加入signMethod=MD5...妈的这个快手
             */
            waitSignParams.Add("signMethod", "MD5");
            #endregion


            #region 添加sign信息
            string sign = CreateSign(waitSignParams, "MD5", this.sign_secret);
            //快手给的java代码改的,也能用,但是没用他的,用的自己写的sign = this.sign(waitSignParams, this.sign_secret, "MD5");
            waitSignParams.Add("sign", sign);
            //waitSignParams.Add("signMethod", "MD5");
            #endregion
            #region 重新构建url
            string realUrl = null;
            if (this.url != null)
            {
                this.url.TrimEnd('?');
                this.url.TrimEnd('/');
                string apiPath = apiName;
                apiPath = apiPath.Replace('.', '/');
                realUrl = string.Format("{0}/{1}", this.url, apiPath);
            }
            #endregion
            string responseStr = wu.DoPost(realUrl, waitSignParams);
            if (responseStr == null || responseStr.Length < 1)
            {
                response.ErrMsg = "无效的请求返回";
                return response;
            }
            try
            {
                response.Body = responseStr;
                //Dictionary<string, object> responseDic = JSON.ToObject<Dictionary<string, object>>(responseStr);
                //#region 正文提取
                //responseStr = responseStr.Substring(1, responseStr.Length - 2);
                //int startPos = responseStr.IndexOf("{");
                //int endPos = responseStr.LastIndexOf("}");
                //responseStr = responseStr.Substring(startPos, endPos - startPos + 1);
                //#endregion
                JSON.FillObject(response, responseStr);
            }
            catch (Exception fillObjErr)
            {
                response.ErrMsg = "返回类型与Response类型不匹配,解析失败" + fillObjErr.Message;
                return response;
            }

            #endregion
            return response;
        }
        private string CreateSign(Dictionary<string, string> parameters, string signMethod, string signSecret)
        {
            // 第一步:把字典按Key的字母顺序排序
            IDictionary<string, string> sortedParams = new SortedDictionary<string, string>(parameters, StringComparer.Ordinal);

            // 第二步:把所有参数名和参数值串在一起
            StringBuilder query = new StringBuilder();
            foreach (KeyValuePair<string, string> kv in sortedParams)
            {
                if (!string.IsNullOrEmpty(kv.Key) && !string.IsNullOrEmpty(kv.Value))
                {
                    if (query.Length > 0)
                    {
                        query.Append("&");
                    }
                    query.AppendFormat("{0}={1}", kv.Key, kv.Value);
                }
            }
            query.AppendFormat("&signSecret={0}", this.sign_secret);
            //query = new StringBuilder("access_token=xxx&appkey=ks123&method=open.xxx.xxx.xxx&param={\"title\":\"短袖\", \"relItemId\":123456,\"categoryId\":12}&signMethod=MD5&timestamp=1583271919000&version=1&signSecret=xxxxxx");
            // 第四步:使用MD5/HMAC加密
            byte[] bytes;
            MD5 md5 = MD5.Create();
            bytes = md5.ComputeHash(Encoding.UTF8.GetBytes(query.ToString()));

            // 第五步:把二进制转化为大写的十六进制
            StringBuilder result = new StringBuilder();
            for (int i = 0; i < bytes.Length; i++)
            {
                result.Append(bytes[i].ToString("x2"));
            }

            return result.ToString();
        }
       
        #region 使用code换取token的时候是使用的另外的连接不是api请求连接.并且请求方式是get,所以做特殊处理
        public Oauth2AccessTokenCreateResult AuthTokenCreate(string code)
        {
            Oauth2AccessTokenCreateResult ret = new Oauth2AccessTokenCreateResult();
            string tokenGetUrl = "https://open.kwaixiaodian.com/oauth2/access_token";
            Dictionary<string, string> param = new Dictionary<string, string>();
            param.Add("app_id", this.client_id);
            param.Add("app_secret", this.client_secret);
            param.Add("grant_type", "code");
            param.Add("code", code);
            WebUtils wu = new WebUtils();
            //tokenGetUrl = wu.BuildGetUrl(tokenGetUrl, param);
            string retJson = wu.DoGet(tokenGetUrl, param);
            JSON.FillObject(ret, retJson);
            return ret;
        }
        #endregion
    }
}

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 11
    评论
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Afterwards_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值