C# .NET对接抖音团购验券核销

C# .NET对接抖音团购验券核销

这里 RPC 使用原生的HTTP Client ,下期写对接饿了么的文章时,再使用Flurl.http进行示例

抖音开发文档链接如下:
https://developer.open-douyin.com/docs/resource/zh-CN/local-life/develop/OpenAPI/life.capacity.fulfilment/certificate.prepare/

验券总结来说就是三步:
1.获取client token
2.验券准备,获取团购券的券码和验券token
3.验券

所有示例代码基于CoreShop

先上基类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CoreCms.Net.Model.Entities.Tiktok
{

    /// <summary>
    /// 抖音配置类
    /// </summary>
    public class TiktokConfig
    {
        public string APPID { get; set; }
        public string AppSecret { get; set; }
        public bool IsSandBox { get; set; }
        public string CallBackUrl { get; set; }

        /// <summary>
        /// 抖音配置类
        /// </summary>
        /// <param name="aPPID"></param>
        /// <param name="appSecret"></param>
        /// <param name="isSandBox"></param>
        /// <exception cref="ArgumentNullException"></exception>
        public TiktokConfig(string aPPID, string appSecret, string callBackUrl, bool isSandBox = false )
        {
            APPID = aPPID ?? throw new ArgumentNullException(nameof(APPID));
            AppSecret = appSecret ?? throw new ArgumentNullException(nameof(AppSecret));
            IsSandBox = isSandBox;
            CallBackUrl = callBackUrl;
        }




    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CoreCms.Net.Model.Entities
{
    /// <summary>
    /// 抖音常量工具类
    /// </summary>
    public class TiktokConst
    {


        /// <summary>
        /// 获取接口调用的凭证client_access_token
        /// </summary>
        public static string CLIENT_TOKEN = "https://open.douyin.com/oauth/client_token/";


        /// <summary>
        /// 抖音团购token
        /// </summary>
        public static string DOUYIN_TOKEN_KEY = "douyin_live_token";

        //抖音生活服务


        /// <summary>
        /// 券详情
        /// </summary>
        public static string CERTIFICATE_GET = "https://open.douyin.com/goodlife/v1/fulfilment/certificate/get/";


        /// <summary>
        /// 券状态批量查询
        /// </summary>
        public static string CERTIFICATE_BATCH_GET = "https://open.douyin.com/goodlife/v1/fulfilment/certificate/query/";


        /// <summary>
        /// 验券准备
        /// </summary>
        public static string CERTIFICATE_PREPARE = "https://open.douyin.com/goodlife/v1/fulfilment/certificate/prepare/";


        /// <summary>
        /// 验券
        /// </summary>
        public static string CERTIFICATE_VERIFY = "https://open.douyin.com/goodlife/v1/fulfilment/certificate/verify/";



        /// <summary>
        /// 撤销核销
        /// </summary>
        public static string CERTIFICATE_CANCEL = "https://open.douyin.com/goodlife/v1/fulfilment/certificate/cancel/";


        /// <summary>
        /// 门店信息查询
        /// </summary>
        public static string SHOP_POI_QUERY = "https://open.douyin.com/goodlife/v1/shop/poi/query/";

        /// <summary>
        /// 门店信息更新
        /// </summary>
        public static string SHOP_POI_UPDATE = "https://open.douyin.com/goodlife/v1/poi/poi/update/";


        /// <summary>
        /// 验券记录查询
        /// </summary>
        public static string VERIFY_RECORD_QUERY = "https://open.douyin.com/goodlife/v1/fulfilment/certificate/verify_record/query/";

        /// <summary>
        /// 账单详细查询
        /// </summary>
        public static string DETAILED_QUERY = "https://open.douyin.com/goodlife/v1/settle/ledger/detailed_query/";

        //*************************** 会员 ***************************
        /// <summary>
        /// 账单详细查询
        /// </summary>
        public static string MEMBER_UPDATE = "https://open.douyin.com/goodlife/v1/member/user/update/";


    }
}


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CoreCms.Net.Model.Entities
{

    /// <summary>
    /// token响应
    /// </summary>
    public class ClientTokenResp
    {

        public Data data { get; set; }
        public string message { get; set; }



        public class Data
        {
            public string access_token { get; set; }
            public string captcha { get; set; }
            public string desc_url { get; set; }
            public string description { get; set; }
            public int error_code { get; set; }
            public int ExpiresIn { get; set; }
            public string LogId { get; set; }
        }

    }
}


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CoreCms.Net.Model.Entities
{
    public class TiktokBaseResp<T>
    {
        public T data { get; set; }
        public Extra extra { get; set; }
        public string message { get; set; }
        public class Extra
        {
            public int error_code { get; set; }
            public string description { get; set; }
            public int sub_error_code { get; set; }
            public string sub_description { get; set; }
            public long now { get; set; }
            public string logid { get; set; }
        }


    }
}



using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static SKIT.FlurlHttpClient.Wechat.Api.Models.CardCreateRequest.Types.GrouponCard.Types.Base.Types;
using static SKIT.FlurlHttpClient.Wechat.Api.Models.ChannelsECOrderGetResponse.Types.Order.Types.OrderDetail.Types;

namespace CoreCms.Net.Model.Entities
{
    /// <summary>
    /// 验券准备返回体
    /// </summary>
    public class CertificatePrepareResp
    {
        /// <summary>
        /// 错误码
        /// </summary>
        public int error_code { get; set; }

        /// <summary>
        /// 错误码描述
        /// </summary>
        public string description { get; set; }

        /// <summary>
        /// 抖音订单id
        /// </summary>

        public string order_id { get; set; }

        /// <summary>
        /// 一次验券的标识, 在验券接口传入
        /// </summary>
        public string verify_token { get; set; }

        /// <summary>
        /// 可用团购券列表
        /// </summary>
        public List<Certificates> certificates { get; set; }
        public class Certificates
        {

            /// <summary>
            /// 加密券码, 在验券接口传入
            /// </summary>
            public string encrypted_code { get; set; }


            /// <summary>
            ///券码有效期,截至时间,时间戳,单位秒
            /// </summary>
            public long expire_time { get; set; }

            /// <summary>
            /// 团购信息
            /// </summary>
            public Sku sku { get; set; }
            public class Sku
            {

                /// <summary>
                /// 团购SKU ID
                /// </summary>
                public string sku_id { get; set; }

                /// <summary>
                /// 团购名称
                /// </summary>
                public string title { get; set; }

                /// <summary>
                /// 团购类型(type=1团餐券; type=2代金券; type=3次卡)
                /// </summary>
                public int groupon_type { get; set; }


                /// <summary>
                /// 团购市场价,单位分
                /// </summary>
                public decimal market_price { get; set; }



                /// <summary>
                /// 团购售卖开始时间,时间戳,单位秒
                /// </summary>
                public long sold_start_time { get; set; }


                /// <summary>
                /// 商家系统(第三方)团购id
                /// </summary>
                public string third_sku_id { get; set; }

                /// <summary>
                /// 商家团购账号id
                /// </summary>
                public string account_id { get; set; }

            }

            /// <summary>
            /// 金额信息
            /// </summary>
            public Amount amount { get; set; }
            public class Amount
            {
                /// <summary>
                /// 券原始金额,单位分
                /// </summary>
                public decimal original_amount { get; set; }

                /// <summary>
                /// 用户实付金额,单位分
                /// </summary>
                public decimal pay_amount { get; set; }


                /// <summary>
                /// 商家营销金额,单位分
                /// </summary>
                public decimal merchant_ticket_amount { get; set; }


                /// <summary>
                /// 支付优惠金额,单位分
                /// </summary>
                public decimal payment_discount_amount { get; set; }


                /// <summary>
                /// 券实付金额(=用户实付金额+支付优惠金额),单位分
                /// </summary>
                public decimal coupon_pay_amount { get; set; }

            }

            /// <summary>
            /// 次卡信息
            /// </summary>
            public TimeCard time_card { get; set; }
            public class TimeCard
            {

                /// <summary>
                /// 次卡总次数
                /// </summary>
                public int times_count { get; set; }


                /// <summary>
                /// 次卡已使用次数
                /// </summary>
                public int times_used { get; set; }
            }
        
        }

        /// <summary>
        /// 商品信息
        /// </summary>
        public corecmsfood productInfo { get; set; }

    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CoreCms.Net.Model.Entities
{
    public class CertificateVerifyResp
    {

        /// <summary>
        /// 错误码
        /// </summary>
        public int error_code;



        /// <summary>
        /// 错误码描述
        /// </summary>
        public string description;

        /// <summary>
        /// 验券结果
        /// </summary>
        public List<VerifyResults> verify_results;
        public class VerifyResults
        {


            /// <summary>
            /// 验券结果码,0表示成功,非0表示失败
            /// </summary>
            public int result { get; set; }

            /// <summary>
            /// 验券结果说明
            /// </summary>
            public string msg { get; set; }


            /// <summary>
            /// 代表验券传入的code或encrypted_code
            /// </summary>
            public string code { get; set; }


            /// <summary>
            /// 代表券码一次核销的标识(撤销时需要)
            /// </summary>
            public string verify_id { get; set; }



            /// <summary>
            ///代表一张券码的标识(撤销时需要)
            /// </summary>
            public string certificate_id { get; set; }



            /// <summary>
            ///代表抖音团购券的12位原始券码(抖音加密券码核销时)
            /// </summary>
            public string origin_code { get; set; }

            /// <summary>
            ///代表企业号商家总店id(查询验券历史时需要)
            /// </summary>
            public string account_id { get; set; }


            /// <summary>
            ///代表一张订单的标识
            /// </summary>
            public string order_id { get; set; }


        }



    }
}

控制器

using CoreCms.Net.Configuration;
using CoreCms.Net.DTO;
using CoreCms.Net.IServices;
using CoreCms.Net.Model.Entities;
using CoreCms.Net.Model.ViewModels.UI;
using CoreCms.Net.Services;
using Microsoft.AspNetCore.Mvc;
using System.ComponentModel;
using System.IO;
using System.Threading.Tasks;
using System;
using CoreCms.Net.Filter;
using CoreCms.Net.WeChat.Service.Utilities;
using Azure.Core;
using CoreCms.Net.Model.Entities.Tiktok;
using CoreCms.Net.Model.FromBody;
using CoreCms.Net.Auth.HttpContextUser;
using Microsoft.AspNetCore.Authorization;

namespace CoreCms.Net.Web.WebApi.Controllers.Tiktok
{

    [Description("抖音控制器")]
    [Route("api/[controller]/[action]")]
    [ApiExplorerSettings(GroupName = "Tiktok ")]
    [ApiController]
    [RequiredErrorForAdmin]
    public class TiktokController : ControllerBase
    {
        private readonly ITiktokServices _tiktokServices;
        private readonly ICoreCmsStoreServices _coreCmsStoreServices;


        public TiktokController( ITiktokServices tiktokServices, ICoreCmsStoreServices coreCmsStoreServices)
        {
            _tiktokServices = tiktokServices;
            _coreCmsStoreServices = coreCmsStoreServices;
        }



        [HttpPost]
        [Description("验券准备")]
        public async Task<TiktokBaseResp<CertificatePrepareResp>> CertificatePrepare(FMData data)
        => await _tiktokServices.CertificatePrepare(data.data);


        [HttpPost]
        [Description("验券准备 ( 根据券码 ) ")]
        public async Task<TiktokBaseResp<CertificatePrepareResp>> CertificatePrepareByCode(FMData data)
        => await _tiktokServices.CertificatePrepareByCode(data.data);

        [HttpPost]
        [Description("获取ClientToken")]
        public async Task<AdminUiCallBack> GetClientToken()
        {
            AdminUiCallBack jm = new()
            {
                code = 0,
                msg = GlobalConstVars.GetDataSuccess,
                data = await _tiktokServices.GetToken()
            };
            return jm;
        }

        [HttpPost]
        [Description("验券")]
        public async Task<TiktokBaseResp<CertificateVerifyResp>> CertificateVerify(CertificateVerifyReq req)
        {
            TiktokBaseResp<CertificateVerifyResp> tiktokBaseResp = await _tiktokServices.CertificateVerify(req);
            return tiktokBaseResp;
        }



    }
}

服务层

using CoreCms.Net.Configuration;
using CoreCms.Net.Model.Entities.Tiktok;
using Google.Protobuf.WellKnownTypes;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Primitives;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using System.Text.Json.Serialization;
using Newtonsoft.Json;
using CoreCms.Net.Services.TikTok;
using SqlSugar.Extensions;
using CoreCms.Net.Caching.Manual;
using CoreCms.Net.Auth.HttpContextUser;
using CoreCms.Net.IServices;
using Org.BouncyCastle.Asn1.Ocsp;
using Essensoft.Paylink.Alipay.Domain;
using CoreCms.Net.Model.Entities;
using Microsoft.IdentityModel.Tokens;
using Aliyun.OSS.Util;
using System.Threading;
using System.Collections.Specialized;
using System.Web;
using CoreCms.Net.Loging;
using NPOI.SS.Formula.Functions;


namespace CoreCms.Net.Services
{
    public class TiktokServices : ITiktokServices
    {
        private readonly TiktokConfig _config;

        public TiktokServices()
        {
            // 读取配置文件 
            var APPID = AppSettingsHelper.GetContent("Tiktok", "APPID");  //抖音服务商的app_ID
            var AppSecret = AppSettingsHelper.GetContent("Tiktok", "AppSecret"); // 抖音服务商的应用秘钥
            var isSandBox = AppSettingsHelper.GetContent("Tiktok", "IsSandBox").ObjToBool();  // 是否沙箱环境
            _config = new TiktokConfig(APPID, AppSecret, callBackUrl, isSandBox);
        }


        /// <summary>
        /// 获取token
        /// </summary>
        /// <returns></returns>
        public async Task<string> GetToken()
        {

            string responseBody = null;
            string clientToken = string.Empty;
     
            ClientTokenResp clientTokenResponse = null;
            try
            {
                using (var client = new HttpClient())
                {
                    string url = "https://open.douyin.com/oauth/client_token/";

                    var jsonData = new
                    {
                        grant_type = "client_credential",
                        client_key = _config.APPID,
                        client_secret = _config.AppSecret
                    };

                    string jsonString = JsonConvert.SerializeObject(jsonData);

                    StringContent content = new StringContent(jsonString, Encoding.UTF8, "application/json");
                    // 这段一定要写
                    content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
                    HttpResponseMessage response = await client.PostAsync(url, content);

                    response.EnsureSuccessStatusCode();

                    responseBody = await response.Content.ReadAsStringAsync();
                    clientTokenResponse = JsonConvert.DeserializeObject<ClientTokenResp>(responseBody);

                    if (clientTokenResponse.data.error_code == 0 && !string.IsNullOrEmpty(clientTokenResponse.data.access_token))
                    {
                        //ManualDataCache.Instance.Set($"tiktok_client_token", clientTokenResponse.data.access_token, 6500);
                        clientToken = clientTokenResponse.data.access_token;
                    }
                }
            }
            catch (HttpRequestException e)
            {
                Console.WriteLine("\nException Caught!");
                Console.WriteLine("Message :{0} ", e.Message);
                NLogUtil.WriteFileLog(NLog.LogLevel.Error, LogType.Other, "Tiktok获取失败", "请求token错误事件", e);
            }
            //}
            return clientToken;
        }


        /// <summary>
        /// 验券准备
        /// </summary>
        /// <param name="code">券码</param>
        /// <returns></returns>
        public async Task<TiktokBaseResp<CertificatePrepareResp>> CertificatePrepare(string shortLink)
        {
            TiktokBaseResp<CertificatePrepareResp> certificatePrepareResp = null;
            // 验券前准备
            try
            {
                string object_id = await GetDouYinUrlParams(shortLink, "object_id");

                using (var client = new HttpClient())
                {
                    string token = await GetToken();
                    //client.DefaultRequestHeaders.Add("access-clientToken", token);
                    client.DefaultRequestHeaders.Add("access-token", token);
                    // 设置请求的URL,包括查询字符串  
                    string url = $"{TiktokConst.CERTIFICATE_PREPARE}?encrypted_data={object_id}";

                    // 发送GET请求  
                    HttpResponseMessage response = await client.GetAsync(url);

                    // 确保HTTP成功状态值  
                    response.EnsureSuccessStatusCode();

                    // 读取响应内容  
                    string responseBody = await response.Content.ReadAsStringAsync();
                    certificatePrepareResp = JsonConvert.DeserializeObject<TiktokBaseResp<CertificatePrepareResp>>(responseBody);

                }
            }
            catch (HttpRequestException e)
            {

                Console.WriteLine("\nException Caught!");
                Console.WriteLine("Message :{0} ", e.Message);
                NLogUtil.WriteFileLog(NLog.LogLevel.Error, LogType.Other, "Tiktok验券准备", "请求错误事件", e);
            }

            return certificatePrepareResp;
        }


        /// <summary>
        /// 核销
        /// </summary>
        /// <param name="code">券码</param>
        /// <returns></returns>

        public async Task<TiktokBaseResp<CertificateVerifyResp>> CertificateVerify(CertificateVerifyReq certificateVerifyReq)
        {
            TiktokBaseResp<CertificateVerifyResp> certificateResponse = null;

            // 验券
            try
            {
                using (var client = new HttpClient())
                {
                    string token = await GetToken();
                    client.DefaultRequestHeaders.Add("access-token", token);

                    // 准备要发送的JSON数据  
                    var jsonData = certificateVerifyReq;


                    // 将JSON数据转换为字符串  
                    string jsonString = JsonConvert.SerializeObject(jsonData);

                    // 创建HttpContent对象,并设置请求的内容类型  
                    StringContent content = new StringContent(jsonString, Encoding.UTF8, "application/json");
                    content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");

                    // 发送POST请求  
                    HttpResponseMessage response = await client.PostAsync(TiktokConst.CERTIFICATE_VERIFY, content);

                    // 确保HTTP成功状态值  
                    response.EnsureSuccessStatusCode();

                    // 读取响应内容  
                    string responseBody = await response.Content.ReadAsStringAsync();
                    Console.WriteLine(responseBody);
                    certificateResponse = JsonConvert.DeserializeObject<TiktokBaseResp<CertificateVerifyResp>>(responseBody);
                }
            }
            catch (HttpRequestException e)
            {
                Console.WriteLine("\nException Caught!");
                Console.WriteLine("Message :{0} ", e.Message);
                NLogUtil.WriteFileLog(NLog.LogLevel.Error, LogType.Other, "Tiktok验券失败", "Tiktok验券失败事件", e);
            }

            return certificateResponse;
        }

        /// <summary>
        /// 获取抖音url参数
        /// </summary>
        /// <param name="url"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        private async Task<string> GetDouYinUrlParams(string shortLink, string key)
        {
            try
            {
                using (var client = new HttpClient())
                {
                    HttpResponseMessage response = await client.GetAsync(shortLink);

                    Uri uri = response.RequestMessage.RequestUri;
                    string v = uri.ObjToString();
                    // 读取响应内容  
                    NameValueCollection queryParameters = HttpUtility.ParseQueryString(uri.Query);

                    return queryParameters[key];
                }
            }
            catch (HttpRequestException e)
            {
                Console.WriteLine("\nException Caught!");
                Console.WriteLine("Message :{0} ", e.Message);
            }
            return null;
        }

    }

}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值