Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
fangqm
RsCode
提交
c675b8b7
R
RsCode
项目概览
fangqm
/
RsCode
10 个月 前同步成功
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
R
RsCode
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
提交
c675b8b7
编写于
5月 28, 2023
作者:
K
kuiyu
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
增加抖音担保支付业务SDK
上级
8936129c
变更
49
隐藏空白更改
内联
并排
Showing
49 changed file
with
2722 addition
and
10 deletion
+2722
-10
.gitignore
.gitignore
+3
-0
RsCode..sln
RsCode..sln
+15
-2
src/RsCode.Douyin/Core/DouyinClient.cs
src/RsCode.Douyin/Core/DouyinClient.cs
+89
-0
src/RsCode.Douyin/Core/DouyinHttpClient.cs
src/RsCode.Douyin/Core/DouyinHttpClient.cs
+57
-0
src/RsCode.Douyin/Core/DouyinOptions.cs
src/RsCode.Douyin/Core/DouyinOptions.cs
+108
-0
src/RsCode.Douyin/Core/DouyinRequest.cs
src/RsCode.Douyin/Core/DouyinRequest.cs
+88
-0
src/RsCode.Douyin/Core/DouyinResponse.cs
src/RsCode.Douyin/Core/DouyinResponse.cs
+18
-0
src/RsCode.Douyin/Core/DouyinResult.cs
src/RsCode.Douyin/Core/DouyinResult.cs
+42
-0
src/RsCode.Douyin/Core/DouyinTool.cs
src/RsCode.Douyin/Core/DouyinTool.cs
+250
-0
src/RsCode.Douyin/Core/HttpHandler.cs
src/RsCode.Douyin/Core/HttpHandler.cs
+61
-0
src/RsCode.Douyin/Core/IDouyinClient.cs
src/RsCode.Douyin/Core/IDouyinClient.cs
+24
-0
src/RsCode.Douyin/Core/RandomGenerator.cs
src/RsCode.Douyin/Core/RandomGenerator.cs
+51
-0
src/RsCode.Douyin/Core/ServiceExtensions.cs
src/RsCode.Douyin/Core/ServiceExtensions.cs
+40
-0
src/RsCode.Douyin/OAuth/AccessTokenRequest.cs
src/RsCode.Douyin/OAuth/AccessTokenRequest.cs
+56
-0
src/RsCode.Douyin/OAuth/AccessTokenResponse.cs
src/RsCode.Douyin/OAuth/AccessTokenResponse.cs
+53
-0
src/RsCode.Douyin/OAuth/Code2SessionRequest.cs
src/RsCode.Douyin/OAuth/Code2SessionRequest.cs
+62
-0
src/RsCode.Douyin/OAuth/Code2SessionResponse.cs
src/RsCode.Douyin/OAuth/Code2SessionResponse.cs
+56
-0
src/RsCode.Douyin/Payment/NpoiOrder.cs
src/RsCode.Douyin/Payment/NpoiOrder.cs
+86
-0
src/RsCode.Douyin/Payment/OrderSyncRequest.cs
src/RsCode.Douyin/Payment/OrderSyncRequest.cs
+86
-0
src/RsCode.Douyin/Payment/OrderSyncResponse.cs
src/RsCode.Douyin/Payment/OrderSyncResponse.cs
+34
-0
src/RsCode.Douyin/Payment/PayNotifyData.cs
src/RsCode.Douyin/Payment/PayNotifyData.cs
+151
-0
src/RsCode.Douyin/Payment/PayQueryRequest.cs
src/RsCode.Douyin/Payment/PayQueryRequest.cs
+50
-0
src/RsCode.Douyin/Payment/PayQueryResponse.cs
src/RsCode.Douyin/Payment/PayQueryResponse.cs
+116
-0
src/RsCode.Douyin/Payment/PoiOrder9001.cs
src/RsCode.Douyin/Payment/PoiOrder9001.cs
+15
-0
src/RsCode.Douyin/Payment/PoiOrder9101.cs
src/RsCode.Douyin/Payment/PoiOrder9101.cs
+144
-0
src/RsCode.Douyin/Payment/PrepayRequest.cs
src/RsCode.Douyin/Payment/PrepayRequest.cs
+131
-0
src/RsCode.Douyin/Payment/PrepayResponse.cs
src/RsCode.Douyin/Payment/PrepayResponse.cs
+44
-0
src/RsCode.Douyin/Payment/RefundNotifyData.cs
src/RsCode.Douyin/Payment/RefundNotifyData.cs
+42
-0
src/RsCode.Douyin/Payment/RefundNotifyResult.cs
src/RsCode.Douyin/Payment/RefundNotifyResult.cs
+68
-0
src/RsCode.Douyin/Payment/RefundQueryRequest.cs
src/RsCode.Douyin/Payment/RefundQueryRequest.cs
+46
-0
src/RsCode.Douyin/Payment/RefundQueryResponse.cs
src/RsCode.Douyin/Payment/RefundQueryResponse.cs
+79
-0
src/RsCode.Douyin/Payment/RefundRequest.cs
src/RsCode.Douyin/Payment/RefundRequest.cs
+81
-0
src/RsCode.Douyin/Payment/RefundResponse.cs
src/RsCode.Douyin/Payment/RefundResponse.cs
+38
-0
src/RsCode.Douyin/RsCode.Douyin.csproj
src/RsCode.Douyin/RsCode.Douyin.csproj
+31
-0
src/RsCode.Douyin/Storage/RemoveUserStorageRequest.cs
src/RsCode.Douyin/Storage/RemoveUserStorageRequest.cs
+54
-0
src/RsCode.Douyin/Storage/RemoveUserStorageResponse.cs
src/RsCode.Douyin/Storage/RemoveUserStorageResponse.cs
+23
-0
src/RsCode.Douyin/Storage/SetUserStorageReqeust.cs
src/RsCode.Douyin/Storage/SetUserStorageReqeust.cs
+53
-0
src/RsCode.Douyin/Storage/SetUserStorageResponse.cs
src/RsCode.Douyin/Storage/SetUserStorageResponse.cs
+41
-0
src/RsCode.Douyin/Usings.cs
src/RsCode.Douyin/Usings.cs
+3
-0
src/RsCode.Douyin/rscode64_64.png
src/RsCode.Douyin/rscode64_64.png
+0
-0
src/RsCode.Storage.Aliyun/AliyunOssService.cs
src/RsCode.Storage.Aliyun/AliyunOssService.cs
+4
-4
src/RsCode.Storage.Aliyun/IAliyunOssService.cs
src/RsCode.Storage.Aliyun/IAliyunOssService.cs
+2
-2
src/RsCode/Log/LogHelper.cs
src/RsCode/Log/LogHelper.cs
+3
-2
tests/RsCode.Douyin.Tests/AccessTokenTest.cs
tests/RsCode.Douyin.Tests/AccessTokenTest.cs
+18
-0
tests/RsCode.Douyin.Tests/RsCode.Douyin.Tests.csproj
tests/RsCode.Douyin.Tests/RsCode.Douyin.Tests.csproj
+45
-0
tests/RsCode.Douyin.Tests/SignTest.cs
tests/RsCode.Douyin.Tests/SignTest.cs
+38
-0
tests/RsCode.Douyin.Tests/Startup.cs
tests/RsCode.Douyin.Tests/Startup.cs
+62
-0
tests/RsCode.Douyin.Tests/Usings.cs
tests/RsCode.Douyin.Tests/Usings.cs
+3
-0
tests/RsCode.Douyin.Tests/log4net.config
tests/RsCode.Douyin.Tests/log4net.config
+58
-0
未找到文件。
.gitignore
浏览文件 @
c675b8b7
...
...
@@ -306,3 +306,6 @@ OpenCover/
upload
Plugins
/tests/RsCode.Storage.Tests/appsettings.json
/tests/RsCode.Douyin.Tests/douyin.json
/tests/RsCode.Douyin.Tests/appsettings.json
/tests/RsCode.Douyin.Tests/cert/douyin
RsCode..sln
浏览文件 @
c675b8b7
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 1
6
VisualStudioVersion = 1
6.0.30523.14
1
# Visual Studio Version 1
7
VisualStudioVersion = 1
7.5.33424.13
1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RsCode.AspNetCore.Tests", "tests\RsCode.AspNetCore.Tests\RsCode.AspNetCore.Tests.csproj", "{0EC076FE-1424-40D1-B27E-7DF86B12123E}"
EndProject
...
...
@@ -19,6 +19,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RsCode.Storage.QiniuStorage
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RsCode.Storage.Tests", "tests\RsCode.Storage.Tests\RsCode.Storage.Tests.csproj", "{4018983D-89AB-4A7F-81DC-77631FC0D3D1}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RsCode.Douyin", "src\RsCode.Douyin\RsCode.Douyin.csproj", "{A12AA294-E96D-4475-B0A6-B793D96E1EFF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RsCode.Douyin.Tests", "tests\RsCode.Douyin.Tests\RsCode.Douyin.Tests.csproj", "{64052172-FEA5-4CB5-90C7-F04743C05D36}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
...
...
@@ -53,6 +57,14 @@ Global
{4018983D-89AB-4A7F-81DC-77631FC0D3D1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4018983D-89AB-4A7F-81DC-77631FC0D3D1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4018983D-89AB-4A7F-81DC-77631FC0D3D1}.Release|Any CPU.Build.0 = Release|Any CPU
{A12AA294-E96D-4475-B0A6-B793D96E1EFF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A12AA294-E96D-4475-B0A6-B793D96E1EFF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A12AA294-E96D-4475-B0A6-B793D96E1EFF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A12AA294-E96D-4475-B0A6-B793D96E1EFF}.Release|Any CPU.Build.0 = Release|Any CPU
{64052172-FEA5-4CB5-90C7-F04743C05D36}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{64052172-FEA5-4CB5-90C7-F04743C05D36}.Debug|Any CPU.Build.0 = Debug|Any CPU
{64052172-FEA5-4CB5-90C7-F04743C05D36}.Release|Any CPU.ActiveCfg = Release|Any CPU
{64052172-FEA5-4CB5-90C7-F04743C05D36}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
...
...
@@ -60,6 +72,7 @@ Global
GlobalSection(NestedProjects) = preSolution
{0EC076FE-1424-40D1-B27E-7DF86B12123E} = {A1F1CC2D-DD98-4784-A2A7-36FF487F0476}
{4018983D-89AB-4A7F-81DC-77631FC0D3D1} = {A1F1CC2D-DD98-4784-A2A7-36FF487F0476}
{64052172-FEA5-4CB5-90C7-F04743C05D36} = {A1F1CC2D-DD98-4784-A2A7-36FF487F0476}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E888CD95-C5BE-484E-A8A1-356835B130AD}
...
...
src/RsCode.Douyin/Core/DouyinClient.cs
0 → 100644
浏览文件 @
c675b8b7
/*
* 项目:抖音开放平台SDK RsCode.Douyin
* 协议:MIT License 2.0
* 作者:河南软商网络科技有限公司
* 项目己托管于
* github
https://github.com/kuiyu/RsCode.git
*/
using
Microsoft.AspNetCore.Http
;
using
Microsoft.Extensions.Logging
;
using
Microsoft.Extensions.Options
;
using
Microsoft.Extensions.Primitives
;
using
System.Text
;
using
System.Text.Encodings.Web
;
using
System.Text.Json
;
namespace
RsCode.Douyin.Core
{
public
class
DouyinClient
:
IDouyinClient
{
List
<
DouyinOptions
>
options
;
DouyinHttpClient
httpClient
;
IHttpContextAccessor
httpContextAccessor
;
ILogger
log
;
public
DouyinClient
(
IOptionsSnapshot
<
List
<
DouyinOptions
>>
options
,
DouyinHttpClient
httpClient
,
IHttpContextAccessor
httpContextAccessor
,
ILogger
<
DouyinClient
>
logger
)
{
this
.
options
=
options
.
Value
;
this
.
httpClient
=
httpClient
;
this
.
httpContextAccessor
=
httpContextAccessor
;
log
=
logger
;
}
DouyinOptions
clientOptions
{
get
;
set
;
}
public
DouyinOptions
UseAppId
(
string
appId
)
{
clientOptions
=
options
.
FirstOrDefault
(
o
=>
o
.
AppId
==
appId
);
if
(
clientOptions
==
null
)
throw
new
Exception
(
$"配置有误,未找到appid=
{
appId
}
配置"
);
var
httpHandler
=
new
HttpHandler
(
clientOptions
);
httpClient
.
LoadHandler
(
httpHandler
);
return
clientOptions
;
}
public
async
Task
<
T
>
SendAsync
<
T
>(
DouyinRequest
request
)
where
T
:
DouyinResponse
{
try
{
if
(
clientOptions
==
null
)
{
throw
new
Exception
(
"未调用UseAppId()方法"
);
}
var
url
=
request
.
GetApiUrl
();
var
method
=
request
.
RequestMethod
().
ToUpper
();
if
(
method
==
"GET"
)
{
var
res
=
await
httpClient
.
GetAsync
<
T
>(
url
);
return
res
;
}
if
(
method
==
"POST"
)
{
string
json
=
JsonSerializer
.
Serialize
(
request
,
request
.
GetType
(),
new
JsonSerializerOptions
{
Encoder
=
JavaScriptEncoder
.
Create
(
System
.
Text
.
Unicode
.
UnicodeRanges
.
All
)
});
HttpContent
httpContent
=
new
StringContent
(
json
,
Encoding
.
UTF8
,
"application/json"
);
var
res
=
await
httpClient
.
PostAsync
<
T
>(
url
,
httpContent
);
return
res
;
}
throw
new
Exception
(
"未定义的请求方式"
);
}
catch
(
Exception
e
)
{
throw
e
;
}
}
public
string
GetIp
()
{
var
httpContext
=
httpContextAccessor
;
var
ip
=
httpContext
.
HttpContext
?.
Request
.
Headers
[
"X-Real-IP"
].
FirstOrDefault
()
??
httpContext
.
HttpContext
?.
Request
.
HttpContext
.
Connection
.
RemoteIpAddress
.
ToString
();
return
string
.
IsNullOrWhiteSpace
(
ip
)
?
"0.0.0.0"
:
ip
;
}
}
}
\ No newline at end of file
src/RsCode.Douyin/Core/DouyinHttpClient.cs
0 → 100644
浏览文件 @
c675b8b7
/*
* 项目:抖音开放平台SDK RsCode.Douyin
* 协议:MIT License 2.0
* 作者:河南软商网络科技有限公司
* 项目己托管于
* github
https://github.com/kuiyu/RsCode.git
*/
using
System.Text.Json
;
namespace
RsCode.Douyin.Core
{
public
class
DouyinHttpClient
{
public
HttpClient
httpClient
{
get
;
private
set
;
}
public
DouyinHttpClient
(
HttpClient
client
)
{
httpClient
=
client
;
}
public
void
LoadHandler
(
HttpHandler
httpHandler
)
{
httpClient
=
new
HttpClient
(
httpHandler
);
httpClient
.
BaseAddress
=
new
Uri
(
"https://open.douyin.com"
);
}
public
async
Task
<
T
>
GetAsync
<
T
>(
string
url
)
where
T
:
DouyinResponse
{
using
(
var
response
=
await
httpClient
.
GetAsync
(
url
))
{
int
statusCode
=
Convert
.
ToInt32
(
response
.
StatusCode
);
using
(
var
responseStream
=
await
response
.
Content
.
ReadAsStreamAsync
())
{
var
res
=
await
response
.
Content
.
ReadAsStringAsync
();
return
await
JsonSerializer
.
DeserializeAsync
<
T
>(
responseStream
);
}
}
}
public
async
Task
<
T
>
PostAsync
<
T
>(
string
url
,
HttpContent
httpContent
)
where
T
:
DouyinResponse
{
using
(
httpContent
)
using
(
var
response
=
await
httpClient
.
PostAsync
(
url
,
httpContent
))
{
var
res
=
await
response
.
Content
.
ReadAsStringAsync
();
return
JsonSerializer
.
Deserialize
<
T
>(
res
);
}
}
}
}
src/RsCode.Douyin/Core/DouyinOptions.cs
0 → 100644
浏览文件 @
c675b8b7
/*
* 项目:抖音开放平台SDK RsCode.Douyin
* 协议:MIT License 2.0
* 作者:河南软商网络科技有限公司
* 项目己托管于
* github
https://github.com/kuiyu/RsCode.git
*/
using
System.Runtime.InteropServices
;
using
System.Text.Json.Serialization
;
namespace
RsCode.Douyin
{
public
class
DouyinOptions
{
/// <summary>
/// 获取或设置抖音开放平台应用 Key。
/// </summary>
[
Newtonsoft
.
Json
.
JsonProperty
(
"appid"
)]
[
JsonPropertyName
(
"appid"
)]
public
string
AppId
{
get
;
set
;
}
=
default
!;
/// <summary>
/// 获取或设置抖音开放平台应用密钥。
/// </summary>
[
Newtonsoft
.
Json
.
JsonProperty
(
"secret"
)]
[
JsonPropertyName
(
"secret"
)]
public
string
Secret
{
get
;
set
;
}
=
default
!;
/// <summary>
/// salt
/// </summary>
[
Newtonsoft
.
Json
.
JsonProperty
(
"salt"
)]
[
JsonPropertyName
(
"salt"
)]
public
string
Salt
{
get
;
set
;
}
=
default
!;
/// <summary>
/// 【某小程序】-【功能】-【支付】-【支付产品】-【支付设置】中获取Token
/// </summary>
[
Newtonsoft
.
Json
.
JsonProperty
(
"token"
)]
[
JsonPropertyName
(
"token"
)]
public
string
Token
{
get
;
set
;
}
=
default
!;
/// <summary>
/// 某应用的私钥路径
/// </summary>
[
Newtonsoft
.
Json
.
JsonProperty
(
"privateKeyPath"
)]
[
JsonPropertyName
(
"privateKeyPath"
)]
public
string
PrivateKeyPath
{
get
;
set
;
}
=
default
!;
/// <summary>
/// 抖音平台的公钥路径
/// </summary>
[
Newtonsoft
.
Json
.
JsonProperty
(
"publicKeyPath"
)]
[
JsonPropertyName
(
"publicKeyPath"
)]
public
string
PublicKeyPath
{
get
;
set
;
}
=
default
!;
public
string
GetPublicKey
()
{
if
(
string
.
IsNullOrWhiteSpace
(
PublicKeyPath
))
{
throw
new
Exception
(
"未配置抖音平台公钥路径"
);
}
var
path
=
Path
.
Combine
(
Environment
.
CurrentDirectory
,
PublicKeyPath
);
if
(
RuntimeInformation
.
IsOSPlatform
(
OSPlatform
.
Windows
))
{
path
=
path
.
Replace
(
"/"
,
"\\"
);
}
else
{
path
=
path
.
Replace
(
"\\"
,
"/"
);
}
if
(!
File
.
Exists
(
path
))
{
throw
new
Exception
(
$"未找到抖音平台公钥文件
{
path
}
"
);
}
string
s
=
File
.
ReadAllText
(
path
);
//.Replace("-----BEGIN PUBLIC KEY-----", "").Replace("-----END PUBLIC KEY-----", "").Replace("\n", "");
return
s
;
}
public
string
GetPrivateKey
()
{
if
(
string
.
IsNullOrWhiteSpace
(
PrivateKeyPath
))
{
throw
new
Exception
(
"未配置抖音应用私钥文件"
);
}
var
path
=
Path
.
Combine
(
Environment
.
CurrentDirectory
,
PrivateKeyPath
);
if
(
RuntimeInformation
.
IsOSPlatform
(
OSPlatform
.
Windows
))
{
path
=
path
.
Replace
(
"/"
,
"\\"
);
}
else
{
path
=
path
.
Replace
(
"\\"
,
"/"
);
}
if
(!
File
.
Exists
(
path
))
{
throw
new
Exception
(
$"未找到抖音应用私钥文件
{
path
}
"
);
}
string
s
=
File
.
ReadAllText
(
path
).
Replace
(
"-----BEGIN PRIVATE KEY-----"
,
""
).
Replace
(
"-----END PRIVATE KEY-----"
,
""
).
Replace
(
"-----BEGIN RSA PRIVATE KEY-----"
,
""
).
Replace
(
"-----END RSA PRIVATE KEY-----"
,
""
).
Replace
(
"\n"
,
""
);
return
s
;
}
}
}
src/RsCode.Douyin/Core/DouyinRequest.cs
0 → 100644
浏览文件 @
c675b8b7
/*
* 项目:抖音开放平台SDK RsCode.Douyin
* 协议:MIT License 2.0
* 作者:河南软商网络科技有限公司
* 项目己托管于
* github
https://github.com/kuiyu/RsCode.git
*/
namespace
RsCode.Douyin
{
public
abstract
class
DouyinRequest
{
public
virtual
string
RequestMethod
()
{
return
"POST"
;
}
public
virtual
string
GetApiUrl
()
{
throw
new
Exception
(
"请配置接口地址"
);
}
/// <summary>
/// 担保支付请求签名算法
/// </summary>
/// <param name="options"></param>
/// <returns></returns>
public
string
CreateParamSign
(
DouyinOptions
options
)
{
List
<
string
>
paramList
=
new
List
<
string
>();
//对属性进行ascii码排序
var
props
=
this
.
GetType
().
GetProperties
();
foreach
(
var
prop
in
props
)
{
//排除不需要参数
if
(
prop
.
Name
==
"AppId"
||
prop
.
Name
==
"ThirdPartyId"
||
prop
.
Name
==
"Sign"
||
prop
.
Name
==
"OtherSettleParams"
)
{
continue
;
}
var
value
=
prop
.
GetValue
(
this
);
if
(
value
==
null
)
{
//paramList.Add("<nil>");
}
else
{
paramList
.
Add
(
value
.
ToString
().
Trim
());
}
}
paramList
.
Add
(
options
.
Salt
);
var
list
=
paramList
.
ToArray
();
Array
.
Sort
(
list
,
string
.
CompareOrdinal
);
string
s
=
""
;
foreach
(
var
obj
in
list
)
{
if
(
obj
==
null
||
string
.
IsNullOrWhiteSpace
(
obj
))
{
continue
;
}
string
value
=
obj
.
ToString
();
value
=
value
.
Trim
();
if
(
value
.
Equals
(
""
)||
value
.
Equals
(
"null"
))
{
continue
;
}
if
(
string
.
IsNullOrEmpty
(
s
))
{
s
=
obj
.
Trim
();
}
else
{
s
+=
$"&
{
obj
}
"
;
}
}
var
result
=
DouyinTool
.
MD5Sign
(
s
);
return
result
;
}
}
}
src/RsCode.Douyin/Core/DouyinResponse.cs
0 → 100644
浏览文件 @
c675b8b7
/*
* 项目:抖音开放平台SDK RsCode.Douyin
* 协议:MIT License 2.0
* 作者:河南软商网络科技有限公司
* 项目己托管于
* github
https://github.com/kuiyu/RsCode.git
*/
using
System.Text.Json.Serialization
;
namespace
RsCode.Douyin.Core
{
public
class
DouyinResponse
{
}
}
src/RsCode.Douyin/Core/DouyinResult.cs
0 → 100644
浏览文件 @
c675b8b7
/*
* 项目:抖音开放平台SDK RsCode.Douyin
* 协议:MIT License 2.0
* 作者:河南软商网络科技有限公司
* 项目己托管于
* github
https://github.com/kuiyu/RsCode.git
*/
using
RsCode.Douyin.Core
;
using
System.Text.Json.Serialization
;
namespace
RsCode.Douyin
{
public
class
DouyinResult
<
T
>
where
T
:
DouyinResponse
{
/// <summary>
/// 错误码
/// </summary>
[
JsonPropertyName
(
"err_no"
)]
public
long
ErrNo
{
get
;
set
;
}
/// <summary>
/// 错误信息
/// </summary>
[
JsonPropertyName
(
"err_tips"
)]
public
string
ErrTips
{
get
;
set
;
}
[
JsonPropertyName
(
"data"
)]
public
T
Data
{
get
;
set
;
}
[
JsonPropertyName
(
"error"
)]
public
int
Error
{
get
;
set
;
}
[
JsonPropertyName
(
"errcode"
)]
public
long
ErrCode
{
get
;
set
;
}
[
JsonPropertyName
(
"errmsg"
)]
public
string
ErrMsg
{
get
;
set
;
}
[
JsonPropertyName
(
"message"
)]
public
string
Message
{
get
;
set
;
}
}
}
src/RsCode.Douyin/Core/DouyinTool.cs
0 → 100644
浏览文件 @
c675b8b7
/*
* 项目:抖音开放平台SDK RsCode.Douyin
* 协议:MIT License 2.0
* 作者:河南软商网络科技有限公司
* 项目己托管于
* github
https://github.com/kuiyu/RsCode.git
*/
using
System.Security.Cryptography.X509Certificates
;
using
System.Security.Cryptography
;
using
System.Text
;
using
Microsoft.AspNetCore.Http
;
using
Org.BouncyCastle.Crypto.Engines
;
using
Org.BouncyCastle.Crypto.Modes
;
using
Org.BouncyCastle.Crypto.Parameters
;
using
Microsoft.Extensions.Primitives
;
namespace
RsCode.Douyin.Core
{
public
class
DouyinTool
{
/// <summary>
/// 生成随机数
/// </summary>
/// <returns></returns>
public
static
string
GetNonceStr
()
{
var
randomGenerator
=
new
RandomGenerator
();
return
randomGenerator
.
GetRandomUInt
().
ToString
();
}
/// <summary>
/// 生成时间戳,标准北京时间,时区为东八区,自1970年1月1日 0点0分0秒以来的秒数
/// </summary>
/// <returns></returns>
public
static
string
GetTimeStamp
()
{
TimeSpan
ts
=
DateTime
.
UtcNow
-
new
DateTime
(
1970
,
1
,
1
,
0
,
0
,
0
,
0
);
return
Convert
.
ToInt64
(
ts
.
TotalSeconds
).
ToString
();
}
/// 计算文件的 SHA256 值
/// </summary>
/// <param name="fileName">要计算 SHA256 值的文件名和路径</param>
/// <returns>SHA256值16进制字符串</returns>
public
static
string
SHA256File
(
string
fileName
)
{
return
HashFile
(
fileName
,
"sha256"
);
}
public
static
string
HashFile
(
FileStream
fs
,
string
algName
=
"sha256"
)
{
using
(
fs
)
{
byte
[]
hashBytes
=
HashData
(
fs
,
algName
);
return
ByteArrayToHexString
(
hashBytes
);
}
}
/// <summary>
/// 计算文件的哈希值
/// </summary>
/// <param name="fileName">要计算哈希值的文件名和路径</param>
/// <param name="algName">算法:sha1,md5</param>
/// <returns>哈希值16进制字符串</returns>
private
static
string
HashFile
(
string
fileName
,
string
algName
)
{
if
(!
System
.
IO
.
File
.
Exists
(
fileName
))
return
string
.
Empty
;
FileStream
fs
=
new
FileStream
(
fileName
,
FileMode
.
Open
,
FileAccess
.
Read
);
byte
[]
hashBytes
=
HashData
(
fs
,
algName
);
fs
.
Close
();
return
ByteArrayToHexString
(
hashBytes
);
}
/// <summary>
/// 字节数组转换为16进制表示的字符串
/// </summary>
private
static
string
ByteArrayToHexString
(
byte
[]
buf
)
{
string
returnStr
=
""
;
if
(
buf
!=
null
)
{
for
(
int
i
=
0
;
i
<
buf
.
Length
;
i
++)
{
returnStr
+=
buf
[
i
].
ToString
(
"X2"
);
}
}
return
returnStr
;
}
/// <summary>
/// 计算哈希值
/// </summary>
/// <param name="stream">要计算哈希值的 Stream</param>
/// <param name="algName">算法:sha1,md5</param>
/// <returns>哈希值字节数组</returns>
private
static
byte
[]
HashData
(
Stream
stream
,
string
algName
)
{
HashAlgorithm
algorithm
;
if
(
algName
==
null
)
{
throw
new
ArgumentNullException
(
"algName 不能为 null"
);
}
if
(
string
.
Compare
(
algName
,
"sha256"
,
true
)
==
0
)
{
algorithm
=
SHA256
.
Create
();
}
else
{
if
(
string
.
Compare
(
algName
,
"md5"
,
true
)
!=
0
)
{
throw
new
Exception
(
"algName 只能使用 sha256 或 md5"
);
}
algorithm
=
System
.
Security
.
Cryptography
.
MD5
.
Create
();
}
return
algorithm
.
ComputeHash
(
stream
);
}
public
static
bool
IsBase64String
(
string
str
)
{
try
{
Convert
.
FromBase64String
(
str
);
return
true
;
}
catch
(
Exception
)
{
return
false
;
}
}
/// <summary>
/// 使用平台公钥对提交的信息进行加密
/// 最终提交请求时,需对敏感信息加密,如身份证、银行卡号。
/// 加密算法是RSA,使用从接口下载到的公钥进行加密,非后台下载到的私钥。
///
/// </summary>
/// <param name="text">要加密的明文</param>
/// <param name="publicKey"> -----BEGIN CERTIFICATE----- 开头的string,转为bytes </param>
/// <returns></returns>
public
static
string
RSAEncrypt
(
string
text
,
byte
[]
publicKey
)
{
using
(
var
x509
=
new
X509Certificate2
(
publicKey
))
{
var
rsaParam
=
x509
.
GetRSAPublicKey
().
ExportParameters
(
false
);
var
rsa
=
new
RSACryptoServiceProvider
();
rsa
.
ImportParameters
(
rsaParam
);
var
buff
=
rsa
.
Encrypt
(
Encoding
.
UTF8
.
GetBytes
(
text
),
true
);
return
Convert
.
ToBase64String
(
buff
);
}
}
public
static
string
AesGcmDecrypt
(
string
AES_KEY
,
string
associatedData
,
string
nonce
,
string
ciphertext
)
{
GcmBlockCipher
gcmBlockCipher
=
new
GcmBlockCipher
(
new
AesEngine
());
AeadParameters
aeadParameters
=
new
AeadParameters
(
new
KeyParameter
(
Encoding
.
UTF8
.
GetBytes
(
AES_KEY
)),
128
,
Encoding
.
UTF8
.
GetBytes
(
nonce
),
Encoding
.
UTF8
.
GetBytes
(
associatedData
));
gcmBlockCipher
.
Init
(
false
,
aeadParameters
);
byte
[]
data
=
Convert
.
FromBase64String
(
ciphertext
);
byte
[]
plaintext
=
new
byte
[
gcmBlockCipher
.
GetOutputSize
(
data
.
Length
)];
int
length
=
gcmBlockCipher
.
ProcessBytes
(
data
,
0
,
data
.
Length
,
plaintext
,
0
);
gcmBlockCipher
.
DoFinal
(
plaintext
,
length
);
return
Encoding
.
UTF8
.
GetString
(
plaintext
);
}
/// <summary>
/// 数据签名
/// </summary>
/// <param name="message"></param>
/// <param name="rsa">签名的证书</param>
/// <returns></returns>
public
static
string
Sign
(
string
message
,
DouyinOptions
options
)
{
string
privateKey
=
options
.
GetPrivateKey
();
byte
[]
keyData
=
Convert
.
FromBase64String
(
privateKey
);
var
rsa
=
RSA
.
Create
();
//适用该方法的版本https://learn.microsoft.com/zh-cn/dotnet/api/system.security.cryptography.asymmetricalgorithm.importpkcs8privatekey?view=net-7.0
// rsa.ImportPkcs8PrivateKey(keyData, out _); java
rsa
.
ImportRSAPrivateKey
(
keyData
,
out
_
);
byte
[]
data
=
System
.
Text
.
Encoding
.
UTF8
.
GetBytes
(
message
);
return
Convert
.
ToBase64String
(
rsa
.
SignData
(
data
,
HashAlgorithmName
.
SHA256
,
RSASignaturePadding
.
Pkcs1
));
}
public
static
int
Price
(
decimal
price
)
{
return
Convert
.
ToInt32
(
price
*
100
);
}
public
static
decimal
Price
(
int
price
)
{
return
decimal
.
Round
(
Convert
.
ToDecimal
(
price
)
/
100
,
2
);
}
/// <summary>
/// rfc3339标准格式时间
/// </summary>
/// <param name="dt"></param>
/// <returns></returns>
public
static
string
ConvertDateTime
(
DateTime
dt
)
{
return
dt
.
ToString
(
"yyyy-MM-dd'T'HH:mm:ss.fffK"
);
}
/// <summary>
/// 获取客户端IP
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public
string
GetIp
(
HttpContext
context
)
{
string
ip
=
"0.0.0.0"
;
if
(
context
!=
null
)
{
ip
=
context
.
Request
.
Headers
[
"X-Forwarded-For"
].
FirstOrDefault
();
if
(
string
.
IsNullOrEmpty
(
ip
))
{
ip
=
context
.
Connection
.
RemoteIpAddress
.
ToString
();
}
}
return
ip
;
}
public
static
string
MD5Sign
(
string
inputStr
)
{
byte
[]
hashBytes
=
MD5
.
Create
().
ComputeHash
(
Encoding
.
UTF8
.
GetBytes
(
inputStr
));
StringBuilder
sb
=
new
StringBuilder
();
foreach
(
byte
b
in
hashBytes
)
{
sb
.
Append
(
b
.
ToString
(
"x2"
));
}
return
sb
.
ToString
();
}
}
}
src/RsCode.Douyin/Core/HttpHandler.cs
0 → 100644
浏览文件 @
c675b8b7
/*
* 项目:抖音开放平台SDK RsCode.Douyin
* 协议:MIT License 2.0
* 作者:河南软商网络科技有限公司
* 项目己托管于
* github
https://github.com/kuiyu/RsCode.git
*/
using
System.Net.Http.Headers
;
using
System.Security.Authentication
;
namespace
RsCode.Douyin.Core
{
public
class
HttpHandler
:
DelegatingHandler
{
DouyinOptions
options
;
public
HttpHandler
(
DouyinOptions
options
)
{
this
.
options
=
options
;
HttpClientHandler
handler
=
new
HttpClientHandler
();
handler
.
ClientCertificateOptions
=
ClientCertificateOption
.
Manual
;
handler
.
SslProtocols
=
SslProtocols
.
Tls12
|
SslProtocols
.
Tls11
|
SslProtocols
.
Tls
;
handler
.
ServerCertificateCustomValidationCallback
=
(
message
,
cert
,
chain
,
errors
)
=>
true
;
InnerHandler
=
handler
;
}
protected
override
async
Task
<
HttpResponseMessage
>
SendAsync
(
HttpRequestMessage
request
,
CancellationToken
cancellationToken
)
{
var
auth
=
await
BuildAuthAsync
(
request
);
request
.
Headers
.
Add
(
"Byte-Authorization"
,
$"SHA256-RSA2048
{
auth
}
"
);
// request.Headers.Add("Content-Type", "application/json");
request
.
Headers
.
UserAgent
.
Add
(
new
ProductInfoHeaderValue
(
new
ProductHeaderValue
(
"Unknown"
)));
request
.
Headers
.
AcceptCharset
.
Add
(
new
StringWithQualityHeaderValue
(
"utf-8"
));
return
await
base
.
SendAsync
(
request
,
cancellationToken
);
}
protected
async
Task
<
string
>
BuildAuthAsync
(
HttpRequestMessage
request
)
{
string
method
=
request
.
Method
.
ToString
();
string
body
=
""
;
if
(
method
==
"POST"
||
method
==
"PUT"
||
method
==
"PATCH"
)
{
var
content
=
request
.
Content
;
body
=
await
content
.
ReadAsStringAsync
();
}
string
uri
=
request
.
RequestUri
.
PathAndQuery
;
var
timestamp
=
DateTimeOffset
.
Now
.
ToUnixTimeSeconds
();
string
nonce
=
DouyinTool
.
GetNonceStr
();
// Path.GetRandomFileName();
string
message
=
$"
{
method
}
\n
{
uri
}
\n
{
timestamp
}
\n
{
nonce
}
\n
{
body
}
\n"
;
string
signature
=
DouyinTool
.
Sign
(
message
,
options
);
return
$"appid=\"
{
options
.
AppId
}
\",nonce_str=\"
{
nonce
}
\",timestamp=\"
{
timestamp
}
\",signature=\"
{
signature
}
\""
;
}
}
}
src/RsCode.Douyin/Core/IDouyinClient.cs
0 → 100644
浏览文件 @
c675b8b7
/*
* 项目:抖音开放平台SDK RsCode.Douyin
* 协议:MIT License 2.0
* 作者:河南软商网络科技有限公司
* 项目己托管于
* github
https://github.com/kuiyu/RsCode.git
*/
using
Microsoft.AspNetCore.Http
;
using
RsCode.Douyin.Core
;
namespace
RsCode.Douyin
{
public
interface
IDouyinClient
{
Task
<
T
>
SendAsync
<
T
>(
DouyinRequest
request
)
where
T
:
DouyinResponse
;
DouyinOptions
UseAppId
(
string
appId
);
string
GetIp
();
}
}
\ No newline at end of file
src/RsCode.Douyin/Core/RandomGenerator.cs
0 → 100644
浏览文件 @
c675b8b7
/*
* 项目:抖音开放平台SDK RsCode.Douyin
* 协议:MIT License 2.0
* 作者:河南软商网络科技有限公司
* 项目己托管于
* github
https://github.com/kuiyu/RsCode.git
*/
using
System.Security.Cryptography
;
namespace
RsCode.Douyin.Core
{
public
class
RandomGenerator
{
readonly
RNGCryptoServiceProvider
csp
;
public
RandomGenerator
()
{
csp
=
new
RNGCryptoServiceProvider
();
}
public
int
Next
(
int
minValue
,
int
maxExclusiveValue
)
{
if
(
minValue
>=
maxExclusiveValue
)
throw
new
ArgumentOutOfRangeException
(
"minValue must be lower than maxExclusiveValue"
);
long
diff
=
(
long
)
maxExclusiveValue
-
minValue
;
long
upperBound
=
uint
.
MaxValue
/
diff
*
diff
;
uint
ui
;
do
{
ui
=
GetRandomUInt
();
}
while
(
ui
>=
upperBound
);
return
(
int
)(
minValue
+
(
ui
%
diff
));
}
public
uint
GetRandomUInt
()
{
var
randomBytes
=
GenerateRandomBytes
(
sizeof
(
uint
));
return
BitConverter
.
ToUInt32
(
randomBytes
,
0
);
}
private
byte
[]
GenerateRandomBytes
(
int
bytesNumber
)
{
byte
[]
buffer
=
new
byte
[
bytesNumber
];
csp
.
GetBytes
(
buffer
);
return
buffer
;
}
}
}
src/RsCode.Douyin/Core/ServiceExtensions.cs
0 → 100644
浏览文件 @
c675b8b7
/*
* 项目:抖音开放平台SDK RsCode.Douyin
* 协议:MIT License 2.0
* 作者:河南软商网络科技有限公司
* 项目己托管于
* github
https://github.com/kuiyu/RsCode.git
*/
using
Microsoft.AspNetCore.Http
;
using
Microsoft.Extensions.Configuration
;
using
Microsoft.Extensions.DependencyInjection
;
using
Microsoft.Extensions.DependencyInjection.Extensions
;
using
Microsoft.Extensions.Options
;
using
RsCode.Douyin.Core
;
namespace
RsCode.Douyin
{
public
static
class
ServiceExtensions
{
public
static
void
AddDouyin
(
this
IServiceCollection
services
)
{
services
.
TryAddSingleton
<
IHttpContextAccessor
,
HttpContextAccessor
>();
services
.
AddHttpClient
<
DouyinHttpClient
>();
services
.
AddScoped
<
IDouyinClient
,
DouyinClient
>();
var
configBuilder
=
new
ConfigurationBuilder
()
.
AddJsonFile
(
"douyin.json"
,
optional
:
true
,
reloadOnChange
:
true
);
var
config
=
configBuilder
.
Build
();
var
app
=
config
.
GetSection
(
"app"
);
services
.
Configure
<
List
<
DouyinOptions
>>(
options
=>
config
.
GetSection
(
"app"
).
Bind
(
options
));
//增加内存缓存
services
.
AddMemoryCache
();
}
}
}
src/RsCode.Douyin/OAuth/AccessTokenRequest.cs
0 → 100644
浏览文件 @
c675b8b7
/*
* 项目:抖音开放平台SDK RsCode.Douyin
* 协议:MIT License 2.0
* 作者:河南软商网络科技有限公司
* 项目己托管于
* github
https://github.com/kuiyu/RsCode.git
*/
using
System.Text.Json.Serialization
;
namespace
RsCode.Douyin.OAuth
{
//https://developer.open-douyin.com/docs/resource/zh-CN/mini-app/develop/server/interface-request-credential/get-access-token
public
class
AccessTokenRequest
:
DouyinRequest
{
public
AccessTokenRequest
()
{
}
public
AccessTokenRequest
(
string
appid
,
string
secret
)
{
AppId
=
appid
;
AppSecret
=
secret
;
}
public
AccessTokenRequest
(
DouyinOptions
options
)
{
AppId
=
options
.
AppId
;
AppSecret
=
options
.
Secret
;
}
/// <summary>
/// 获取或设置应用 ID
/// </summary>
[
JsonPropertyName
(
"appid"
)]
public
string
AppId
{
get
;
set
;
}
/// <summary>
/// 获取或设置应用密钥。
/// </summary>
[
JsonPropertyName
(
"secret"
)]
public
string
AppSecret
{
get
;
set
;
}
/// <summary>
///获取 access_token 时值为 client_credential
/// </summary>
[
JsonPropertyName
(
"grant_type"
)]
public
string
GrantType
{
get
;
set
;
}
=
"client_credential"
;
public
override
string
GetApiUrl
()
{
//https://open-sandbox.douyin.com/api/apps/v2/token
return
"https://developer.toutiao.com/api/apps/v2/token"
;
}
}
}
src/RsCode.Douyin/OAuth/AccessTokenResponse.cs
0 → 100644
浏览文件 @
c675b8b7
/*
* 项目:抖音开放平台SDK RsCode.Douyin
* 协议:MIT License 2.0
* 作者:河南软商网络科技有限公司
* 项目己托管于
* github
https://github.com/kuiyu/RsCode.git
*/
using
Newtonsoft.Json
;
using
RsCode.Douyin.Core
;
using
System.Text.Json.Serialization
;
namespace
RsCode.Douyin.OAuth
{
//https://developer.open-douyin.com/docs/resource/zh-CN/mini-app/develop/server/interface-request-credential/get-access-token
public
class
AccessTokenResponse
:
DouyinResponse
{
/// <summary>
/// 错误码
/// </summary>
[
JsonPropertyName
(
"err_no"
)]
public
long
ErrNo
{
get
;
set
;
}
/// <summary>
/// 错误信息
/// </summary>
[
JsonPropertyName
(
"err_tips"
)]
public
string
ErrTips
{
get
;
set
;
}
[
JsonPropertyName
(
"data"
)]
public
AccessTokenResult
Data
{
get
;
set
;
}
}
public
class
AccessTokenResult
{
/// <summary>
///获取的 access_token
/// </summary>
[
JsonPropertyName
(
"access_token"
)]
[
JsonProperty
(
"access_token"
)]
public
string
AccessToken
{
get
;
set
;
}
/// <summary>
/// access_token 有效时间,单位:秒
/// </summary>
[
JsonPropertyName
(
"expires_in"
)]
[
JsonProperty
(
"expires_in"
)]
public
int
ExpiresIn
{
get
;
set
;
}
}
}
src/RsCode.Douyin/OAuth/Code2SessionRequest.cs
0 → 100644
浏览文件 @
c675b8b7
/*
* 项目:抖音开放平台SDK RsCode.Douyin
* 协议:MIT License 2.0
* 作者:河南软商网络科技有限公司
* 项目己托管于
* github
https://github.com/kuiyu/RsCode.git
*/
using
System.Text.Json.Serialization
;
namespace
RsCode.Douyin.OAuth
{
/// <summary>
/// <see cref="https://developer.open-douyin.com/docs/resource/zh-CN/mini-app/develop/server/log-in/code-2-session"/>
/// </summary>
public
class
Code2SessionRequest
:
DouyinRequest
{
public
Code2SessionRequest
(
DouyinOptions
options
,
string
code
,
string
anonymousCode
)
{
AppId
=
options
.
AppId
;
AppSecret
=
options
.
Secret
;
if
(!
string
.
IsNullOrWhiteSpace
(
code
))
{
Code
=
code
;
}
if
(!
string
.
IsNullOrWhiteSpace
(
anonymousCode
))
{
AnonymousCode
=
anonymousCode
;
}
}
/// <summary>
/// 小程序 ID
/// </summary>
[
JsonPropertyName
(
"appid"
)]
public
string
AppId
{
get
;
set
;
}
/// <summary>
/// 小程序的 APP Secret,可以在开发者后台获取
/// </summary>
[
JsonPropertyName
(
"secret"
)]
public
string
AppSecret
{
get
;
set
;
}
/// <summary>
///获取 access_token 时值为 client_credential
/// </summary>
[
JsonPropertyName
(
"code"
)]
public
string
Code
{
get
;
set
;
}
/// <summary>
/// login 接口返回的匿名登录凭证
/// </summary>
[
JsonPropertyName
(
"anonymous_code"
)]
public
string
AnonymousCode
{
get
;
set
;
}
public
override
string
GetApiUrl
()
{
//沙盒地址 https://open-sandbox.douyin.com/api/apps/v2/jscode2session
return
"https://developer.toutiao.com/api/apps/v2/jscode2session"
;
}
}
}
src/RsCode.Douyin/OAuth/Code2SessionResponse.cs
0 → 100644
浏览文件 @
c675b8b7
/*
* 项目:抖音开放平台SDK RsCode.Douyin
* 协议:MIT License 2.0
* 作者:河南软商网络科技有限公司
* 项目己托管于
* github
https://github.com/kuiyu/RsCode.git
*/
using
RsCode.Douyin.Core
;
using
System.Text.Json.Serialization
;
namespace
RsCode.Douyin.OAuth
{
public
class
Code2SessionResponse
:
DouyinResponse
{
/// <summary>
/// 错误码
/// </summary>
[
JsonPropertyName
(
"err_no"
)]
public
long
ErrNo
{
get
;
set
;
}
/// <summary>
/// 错误信息
/// </summary>
[
JsonPropertyName
(
"err_tips"
)]
public
string
ErrTips
{
get
;
set
;
}
[
JsonPropertyName
(
"data"
)]
public
Code2SessionResult
Data
{
get
;
set
;
}
}
public
class
Code2SessionResult
{
/// <summary>
/// 会话密钥,如果请求时有 code 参数才会返回
/// </summary>
[
JsonPropertyName
(
"session_key"
)]
public
string
SessionKey
{
get
;
set
;
}
/// <summary>
/// 用户在当前小程序的 ID,如果请求时有 code 参数才会返回
/// </summary>
[
JsonPropertyName
(
"openid"
)]
public
string
OpenId
{
get
;
set
;
}
/// <summary>
/// 匿名用户在当前小程序的 ID,如果请求时有 anonymous_code 参数才会返回
/// </summary>
[
JsonPropertyName
(
"anonymous_openid"
)]
public
string
AnonymousOpenId
{
get
;
set
;
}
/// <summary>
/// 用户在小程序平台的唯一标识符,请求时有 code 参数才会返回。如果开发者拥有多个小程序,可通过 unionid 来区分用户的唯一性。
/// </summary>
[
JsonPropertyName
(
"unionid"
)]
public
string
UnionId
{
get
;
set
;
}
}
}
src/RsCode.Douyin/Payment/NpoiOrder.cs
0 → 100644
浏览文件 @
c675b8b7
/*
* 项目:抖音开放平台SDK RsCode.Douyin
* 协议:MIT License 2.0
* 作者:河南软商网络科技有限公司
* 项目己托管于
* github
https://github.com/kuiyu/RsCode.git
*/
using
System.Text.Json.Serialization
;
namespace
RsCode.Douyin.Payment
{
public
class
NpoiOrder
{
/// <summary>
/// 开发者侧业务单号。用作幂等控制。该订单号是和担保支付的支付单号绑定的,也就是预下单时传入的 out_order_no 字段,长度 <= 64byte
/// </summary>
[
JsonPropertyName
(
"order_id"
)]
public
string
OrderId
{
get
;
set
;
}
/// <summary>
/// 订单创建的时间,13 位毫秒时间戳
/// </summary>
[
JsonPropertyName
(
"create_time"
)]
public
long
CreateTime
{
get
;
set
;
}
/// <summary>
///订单状态
/// </summary>
[
JsonPropertyName
(
"status"
)]
public
string
Status
{
get
;
set
;
}
/// <summary>
/// 订单商品总数
/// </summary>
[
JsonPropertyName
(
"amount"
)]
public
int
Amount
{
get
;
set
;
}
/// <summary>
/// 订单总价,单位为分
/// </summary>
[
JsonPropertyName
(
"total_price"
)]
public
int
TotalPrice
{
get
;
set
;
}
/// <summary>
/// 小程序订单详情页 path,长度<=1024 byte
/// </summary>
[
JsonPropertyName
(
"detail_url"
)]
public
string
DetailUrl
{
get
;
set
;
}
/// <summary>
/// 子订单商品列表,不可为空
/// </summary>
[
JsonPropertyName
(
"item_list"
)]
public
NpoiItem
[]
ItemList
{
get
;
set
;
}
}
public
class
NpoiItem
{
/// <summary>
/// 开发者侧商品 ID,长度 <= 64 byte
/// </summary>
[
JsonPropertyName
(
"item_code"
)]
public
string
ItemCode
{
get
;
set
;
}
/// <summary>
/// 子订单商品图片 URL, 长度 <= 512 byte
/// </summary>
[
JsonPropertyName
(
"img"
)]
public
string
Img
{
get
;
set
;
}
/// <summary>
/// 子订单商品介绍标题,长度 <= 256 byte
/// </summary>
[
JsonPropertyName
(
"title"
)]
public
string
Title
{
get
;
set
;
}
/// <summary>
/// 子订单商品介绍副标题,长度 <= 256 byte
/// </summary>
[
JsonPropertyName
(
"sub_title"
)]
public
string
SubTitle
{
get
;
set
;
}
/// <summary>
/// 单类商品的数目
/// </summary>
[
JsonPropertyName
(
"amount"
)]
public
int
Amount
{
get
;
set
;
}
/// <summary>
/// 单类商品的总价,单位为分
/// </summary>
[
JsonPropertyName
(
"price"
)]
public
int
Price
{
get
;
set
;
}
}
}
src/RsCode.Douyin/Payment/OrderSyncRequest.cs
0 → 100644
浏览文件 @
c675b8b7
/*
* 项目:抖音开放平台SDK RsCode.Douyin
* 协议:MIT License 2.0
* 作者:河南软商网络科技有限公司
* 项目己托管于
* github
https://github.com/kuiyu/RsCode.git
*/
using
System.Text.Json.Serialization
;
namespace
RsCode.Douyin.Payment
{
public
class
OrderSyncRequest
:
DouyinRequest
{
public
override
string
GetApiUrl
()
{
return
"https://developer.toutiao.com/api/apps/order/v2/push"
;
}
/// <summary>
/// 第三方在抖音开放平台申请的 ClientKey
///注意:POI 订单必传
/// </summary>
[
JsonPropertyName
(
"client_key"
)]
public
string
ClientKey
{
get
;
set
;
}
/// <summary>
/// 服务端 API 调用标识,通过 getAccessToken 获取
/// </summary>
[
JsonPropertyName
(
"access_token"
)]
public
string
AccessToken
{
get
;
set
;
}
/// <summary>
/// POI 店铺同步时使用的开发者侧店铺 ID,购买店铺 ID,长度 < 256 byte 注意:POI 订单必传
/// </summary>
[
JsonPropertyName
(
"ext_shop_id"
)]
public
int
ExtShopId
{
get
;
set
;
}
/// <summary>
/// 做订单展示的字节系 app 名称,目前为固定值“douyin”
/// </summary>
[
JsonPropertyName
(
"app_name"
)]
public
string
AppName
{
get
;
set
;
}
=
"douyin"
;
/// <summary>
/// 小程序用户的 open_id,通过 code2Session 获取
/// </summary>
[
JsonPropertyName
(
"open_id"
)]
public
string
OpenId
{
get
;
set
;
}
/// <summary>
/// json string,根据不同订单类型有不同的结构体,请参见 order_detail 字段说明(json string)
/// </summary>
[
JsonPropertyName
(
"order_detail"
)]
public
string
OrderDetail
{
get
;
set
;
}
/// <summary>
/// 普通小程序订单订单状态,POI 订单可以忽略
///0:待支付
///1:已支付
///2:已取消(用户主动取消或者超时未支付导致的关单)
///4:已核销(核销状态是整单核销,即一笔订单买了 3 个券,核销是指 3 个券核销的整单)
///5:退款中
///6:已退款
///8:退款失败
///注意:普通小程序订单必传,担保支付分账依赖该状态
/// </summary>
[
JsonPropertyName
(
"order_status"
)]
public
int
OrderStatus
{
get
;
set
;
}
/// <summary>
/// 订单类型,枚举值:
///0:普通小程序订单(非POI订单)
///9101:团购券订单(POI 订单)
///9001:景区门票订单(POI订单)
/// </summary>
[
JsonPropertyName
(
"order_type"
)]
public
int
OrderType
{
get
;
set
;
}
/// <summary>
/// 订单信息变更时间,13 位毫秒级时间戳
/// </summary>
[
JsonPropertyName
(
"update_time"
)]
public
int
UpdateTime
{
get
;
set
;
}
/// <summary>
/// 自定义字段,用于关联具体业务场景下的特殊参数,长度 < 2048byte
/// </summary>
[
JsonPropertyName
(
"extra"
)]
public
string
Extra
{
get
;
set
;
}
}
}
src/RsCode.Douyin/Payment/OrderSyncResponse.cs
0 → 100644
浏览文件 @
c675b8b7
/*
* 项目:抖音开放平台SDK RsCode.Douyin
* 协议:MIT License 2.0
* 作者:河南软商网络科技有限公司
* 项目己托管于
* github
https://github.com/kuiyu/RsCode.git
*/
using
System.Text.Json.Serialization
;
namespace
RsCode.Douyin.Payment
{
/// <summary>
/// https://developer.open-douyin.com/docs/resource/zh-CN/mini-app/develop/server/ecpay/order/order-sync
/// </summary>
public
class
OrderSyncResponse
:
DouyinResponse
{
/// <summary>
/// 返回码,详见错误码
/// </summary>
[
JsonPropertyName
(
"err_code"
)]
public
int
ErrCode
{
get
;
set
;
}
/// <summary>
/// 错误信息
/// </summary>
[
JsonPropertyName
(
"err_msg"
)]
public
string
ErrMsg
{
get
;
set
;
}
/// <summary>
/// POI 等关联业务推送结果,非 POI 订单为空,JSON 字符串
/// </summary>
[
JsonPropertyName
(
"body"
)]
public
string
Body
{
get
;
set
;
}
}
}
src/RsCode.Douyin/Payment/PayNotifyData.cs
0 → 100644
浏览文件 @
c675b8b7
/*
* 项目:抖音开放平台SDK RsCode.Douyin
* 协议:MIT License 2.0
* 作者:河南软商网络科技有限公司
* 项目己托管于
* github
https://github.com/kuiyu/RsCode.git
*/
using
System.Security.Cryptography
;
using
System.Text
;
using
System.Text.Json
;
using
System.Text.Json.Serialization
;
namespace
RsCode.Douyin.Payment
{
public
class
PayNotifyData
{
/// <summary>
/// Unix 时间戳,字符串类型
/// </summary>
[
JsonPropertyName
(
"timestamp"
)]
public
string
Timestamp
{
get
;
set
;
}
/// <summary>
/// 随机数
/// </summary>
[
JsonPropertyName
(
"nonce"
)]
public
string
Nonce
{
get
;
set
;
}
/// <summary>
/// 订单信息的 json 字符串
/// </summary>
[
JsonPropertyName
(
"msg"
)]
public
string
Msg
{
get
;
set
;
}
/// <summary>
/// 签名
/// </summary>
[
JsonPropertyName
(
"msg_signature"
)]
public
string
MsgSignature
{
get
;
set
;
}
/// <summary>
/// 回调类型标记,支付成功回调为"payment"
/// </summary>
[
JsonPropertyName
(
"type"
)]
public
string
Type
{
get
;
set
;
}
/// <summary>
/// 担保支付回调签名算法
/// 所有字段(验证时注意不包含 sign 签名本身,不包含空字段与 type 常量字段)内容与平台上配置的 token
/// </summary>
/// <param name="options"></param>
/// <returns></returns>
public
bool
VerifySign
(
DouyinOptions
options
)
{
SortedDictionary
<
string
,
string
>
dics
=
new
SortedDictionary
<
string
,
string
>();
dics
.
Add
(
String2Unicode
(
Timestamp
),
Timestamp
);
dics
.
Add
(
String2Unicode
(
Nonce
),
Nonce
);
dics
.
Add
(
String2Unicode
(
Msg
),
Msg
);
dics
.
Add
(
String2Unicode
(
options
.
Token
),
options
.
Token
);
string
signSource
=
""
;
foreach
(
var
item
in
dics
)
{
signSource
+=
item
.
Value
;
}
byte
[]
StrRes
=
Encoding
.
Default
.
GetBytes
(
signSource
);
HashAlgorithm
iSHA
=
new
SHA1CryptoServiceProvider
();
StrRes
=
iSHA
.
ComputeHash
(
StrRes
);
StringBuilder
signBuilder
=
new
StringBuilder
();
foreach
(
byte
iByte
in
StrRes
)
{
signBuilder
.
AppendFormat
(
"{0:x2}"
,
iByte
);
}
var
sign
=
signBuilder
.
ToString
();
return
sign
==
MsgSignature
;
}
/// <summary>
/// 字符串转Unicode
/// </summary>
/// <param name="source">源字符串</param>
/// <returns>Unicode编码后的字符串</returns>
private
string
String2Unicode
(
string
source
)
{
var
bytes
=
Encoding
.
Unicode
.
GetBytes
(
source
);
var
stringBuilder
=
new
StringBuilder
();
for
(
var
i
=
0
;
i
<
bytes
.
Length
;
i
+=
2
)
{
stringBuilder
.
AppendFormat
(
"\\u{0}{1}"
,
bytes
[
i
+
1
].
ToString
(
"x"
).
PadLeft
(
2
,
'0'
),
bytes
[
i
].
ToString
(
"x"
).
PadLeft
(
2
,
'0'
));
}
return
stringBuilder
.
ToString
();
}
/// <summary>
/// byte数组转换成十六进制字符串
/// </summary>
/// <param name="byteArray"></param>
/// <returns></returns>
private
string
bytesToHexStr
(
byte
[]
byteArray
)
{
StringBuilder
sb
=
new
StringBuilder
();
foreach
(
byte
b
in
byteArray
)
{
sb
.
Append
(
b
.
ToString
(
"X2"
));
}
return
sb
.
ToString
();
}
/// <summary>
/// 用MD5加密字符串
/// </summary>
/// <param name="jsonData">待加密的字符串</param>
/// <returns></returns>
public
string
MD5Encrypt
(
string
jsonData
)
{
MD5CryptoServiceProvider
md5Hasher
=
new
MD5CryptoServiceProvider
();
byte
[]
hashedDataBytes
;
hashedDataBytes
=
md5Hasher
.
ComputeHash
(
Encoding
.
GetEncoding
(
"gb2312"
).
GetBytes
(
jsonData
));
StringBuilder
tmp
=
new
StringBuilder
();
foreach
(
byte
i
in
hashedDataBytes
)
{
tmp
.
Append
(
i
.
ToString
(
"x2"
));
}
return
tmp
.
ToString
();
}
}
public
class
PayNotifyOrderInfo
{
[
JsonPropertyName
(
"appid"
)]
public
string
AppId
{
get
;
set
;
}
[
JsonPropertyName
(
"cp_orderno"
)]
public
string
CpOrderNo
{
get
;
set
;
}
[
JsonPropertyName
(
"cp_extra"
)]
public
string
CpExtra
{
get
;
set
;
}
[
JsonPropertyName
(
"way"
)]
public
string
Way
{
get
;
set
;
}
[
JsonPropertyName
(
"payment_order_no"
)]
public
string
PaymentOrderNo
{
get
;
set
;
}
[
JsonPropertyName
(
"total_amount"
)]
public
int
TotalAmount
{
get
;
set
;
}
[
JsonPropertyName
(
"status"
)]
public
string
Status
{
get
;
set
;
}
[
JsonPropertyName
(
"seller_uid"
)]
public
string
SellerUid
{
get
;
set
;
}
[
JsonPropertyName
(
"extra"
)]
public
object
Extra
{
get
;
set
;
}
[
JsonPropertyName
(
"item_id"
)]
public
string
ItemId
{
get
;
set
;
}
}
}
src/RsCode.Douyin/Payment/PayQueryRequest.cs
0 → 100644
浏览文件 @
c675b8b7
/*
* 项目:抖音开放平台SDK RsCode.Douyin
* 协议:MIT License 2.0
* 作者:河南软商网络科技有限公司
* 项目己托管于
* github
https://github.com/kuiyu/RsCode.git
*/
using
System.Text.Json.Serialization
;
namespace
RsCode.Douyin.Payment
{
/// <summary>
/// 支付结果查询
/// <see cref="https://developer.open-douyin.com/docs/resource/zh-CN/mini-app/develop/server/ecpay/pay-list/query"/>
/// </summary>
public
class
PayQueryRequest
:
DouyinRequest
{
public
override
string
GetApiUrl
()
{
return
"https://developer.toutiao.com/api/apps/ecpay/v1/query_order"
;
}
public
PayQueryRequest
(
string
appId
,
string
orderNo
,
string
thirdPartyId
=
""
)
{
AppId
=
appId
;
OutOrderNo
=
orderNo
;
ThirdPartyId
=
thirdPartyId
;
}
/// <summary>
/// 小程序APPID
/// </summary>
[
JsonPropertyName
(
"app_id"
)]
public
string
AppId
{
get
;
set
;
}
/// <summary>
/// 开发者侧的订单号, 同一小程序下不可重复
/// </summary>
[
JsonPropertyName
(
"out_order_no"
)]
public
string
OutOrderNo
{
get
;
set
;
}
/// <summary>
///签名
/// </summary>
[
JsonPropertyName
(
"sign"
)]
public
string
Sign
{
get
;
set
;
}
/// <summary>
/// 第三方平台服务商 id,非服务商模式留空
/// </summary>
[
JsonPropertyName
(
"thirdparty_id"
)]
public
string
ThirdPartyId
{
get
;
set
;
}
}
}
src/RsCode.Douyin/Payment/PayQueryResponse.cs
0 → 100644
浏览文件 @
c675b8b7
/*
* 项目:抖音开放平台SDK RsCode.Douyin
* 协议:MIT License 2.0
* 作者:河南软商网络科技有限公司
* 项目己托管于
* github
https://github.com/kuiyu/RsCode.git
*/
using
System.Text.Json.Serialization
;
namespace
RsCode.Douyin.Payment
{
public
class
PayQueryResponse
:
DouyinResponse
{
/// <summary>
/// 返回码,详见错误码
/// </summary>
[
JsonPropertyName
(
"err_no"
)]
public
int
ErrNo
{
get
;
set
;
}
/// <summary>
/// 返回码描述,详见错误码描述
/// </summary>
[
JsonPropertyName
(
"err_tips"
)]
public
string
ErrTips
{
get
;
set
;
}
/// <summary>
/// 开发者侧的订单号
/// </summary>
[
JsonPropertyName
(
"out_order_no"
)]
public
string
OutOrderNo
{
get
;
set
;
}
/// <summary>
/// 抖音侧的订单号
/// </summary>
[
JsonPropertyName
(
"order_id"
)]
public
string
OrderId
{
get
;
set
;
}
/// <summary>
/// 支付信息
/// </summary>
[
JsonPropertyName
(
"payment_info"
)]
public
PaymentInfo
PaymentInfo
{
get
;
set
;
}
[
JsonPropertyName
(
"cps_info"
)]
public
CpsInfo
CpsInfo
{
get
;
set
;
}
}
public
class
PaymentInfo
{
/// <summary>
/// 支付金额,单位为分
/// </summary>
[
JsonPropertyName
(
"total_fee"
)]
public
int
TotalFee
{
get
;
set
;
}
/// <summary>
/// 支付状态枚举值:
/// SUCCESS:成功 TIMEOUT:超时未支付 PROCESSING:处理中 FAIL:失败
/// </summary>
[
JsonPropertyName
(
"order_status"
)]
public
string
OrderStatus
{
get
;
set
;
}
/// <summary>
/// 支付时间, 格式为"yyyy-MM-dd hh:mm:ss"
/// </summary>
[
JsonPropertyName
(
"pay_time"
)]
public
string
PayTime
{
get
;
set
;
}
/// <summary>
/// 支付渠道, 1-微信支付,2-支付宝支付,10-抖音支付
/// </summary>
[
JsonPropertyName
(
"way"
)]
public
int
Way
{
get
;
set
;
}
/// <summary>
/// 支付渠道侧的支付单号
/// </summary>
[
JsonPropertyName
(
"channel_no"
)]
public
string
ChannelNo
{
get
;
set
;
}
/// <summary>
/// 该笔交易卖家商户号
/// </summary>
[
JsonPropertyName
(
"seller_uid"
)]
public
string
SellerUid
{
get
;
set
;
}
/// <summary>
/// 订单来源视频对应视频 id
/// </summary>
[
JsonPropertyName
(
"item_id"
)]
public
string
ItemId
{
get
;
set
;
}
/// <summary>
/// 开发者自定义字段
/// </summary>
[
JsonPropertyName
(
"cp_extra"
)]
public
string
CpExtra
{
get
;
set
;
}
}
public
class
CpsInfo
{
// <summary>
/// 达人分佣金额,单位为分。后续商户在进行分账时需要注意可分账金额应扣除达人分佣金额。
//注意:由于订单归因与佣金计算存在延迟,支付成功后立即查询可能未计算完成,建议开发者在支付成功后分账前再进行查询。
/// </summary>
[
JsonPropertyName
(
"share_amount"
)]
public
string
ShareAmount
{
get
;
set
;
}
// <summary>
/// 达人抖音号
/// </summary>
[
JsonPropertyName
(
"douyin_id"
)]
public
string
DouyinId
{
get
;
set
;
}
// <summary>
/// 达人昵称
/// </summary>
[
JsonPropertyName
(
"nickname"
)]
public
string
NickName
{
get
;
set
;
}
}
}
src/RsCode.Douyin/Payment/PoiOrder9001.cs
0 → 100644
浏览文件 @
c675b8b7
using
System
;
using
System.Collections.Generic
;
using
System.Linq
;
using
System.Text
;
using
System.Threading.Tasks
;
namespace
RsCode.Douyin.Payment
{
/// <summary>
/// https://developer.open-douyin.com/docs/resource/zh-CN/mini-app/develop/server/ecpay/order/order-sync#_接口说明
/// </summary>
public
class
PoiOrder9001
{
}
}
src/RsCode.Douyin/Payment/PoiOrder9101.cs
0 → 100644
浏览文件 @
c675b8b7
/*
* 项目:抖音开放平台SDK RsCode.Douyin
* 协议:MIT License 2.0
* 作者:河南软商网络科技有限公司
* 项目己托管于
* github
https://github.com/kuiyu/RsCode.git
*/
using
System.Text.Json.Serialization
;
namespace
RsCode.Douyin.Payment
{
/// <summary>
/// 9101团购券类型
/// https://developer.open-douyin.com/docs/resource/zh-CN/mini-app/develop/server/ecpay/order/order-sync#_接口说明
/// </summary>
public
class
PoiOrder9101
{
/// <summary>
/// 开发者侧业务单号。用作幂等控制。该订单号是和担保支付的支付单号绑定的,也就是预下单时传入的 out_order_no 字段,长度 <= 64byte
/// </summary>
[
JsonPropertyName
(
"ext_order_id"
)]
public
string
ExtOrderId
{
get
;
set
;
}
/// <summary>
///状态
///枚举值:
///10:已取消(抖音订单中心可看到,状态为"已取消")
///110:待支付
///310:未使用
///340:已使用
///410:退款中
///420: 退款成功
///430: 退款失败
/// </summary>
[
JsonPropertyName
(
"status"
)]
public
int
Status
{
get
;
set
;
}
/// <summary>
/// 商铺名字,长度 <= 256 byte
/// </summary>
[
JsonPropertyName
(
"shop_name"
)]
public
string
ShopName
{
get
;
set
;
}
/// <summary>
///订单详情页的外链跳转类型,通过该接口上传的都为 2
///1:H5
///2:抖音小程序
/// </summary>
[
JsonPropertyName
(
"entry_type"
)]
public
int
EntryType
{
get
;
set
;
}
/// <summary>
/// 订单详情页的外链跳转 schema 参数,格式为 json 字符串。长度 <= 512byte,具体参数详见 entry_schema 说明
/// </summary>
[
JsonPropertyName
(
"entry_schema"
)]
public
string
EntrySchema
{
get
;
set
;
}
/// <summary>
/// 下单时间(13位毫秒时间戳)
/// </summary>
[
JsonPropertyName
(
"create_order_time"
)]
public
int
CreateOrderTime
{
get
;
set
;
}
/// <summary>
/// 订单描述,长度<=500 byte
/// </summary>
[
JsonPropertyName
(
"description"
)]
public
string
Description
{
get
;
set
;
}
/// <summary>
/// 订单总金额(单位:分)
/// </summary>
[
JsonPropertyName
(
"total_price"
)]
public
int
TotalPrice
{
get
;
set
;
}
/// <summary>
/// 支付时间(13位毫秒时间戳),未付款时不用传
/// </summary>
[
JsonPropertyName
(
"pay_time"
)]
public
string
PayTime
{
get
;
set
;
}
/// <summary>
/// 开发者侧卡劵核销门店ID,未核销时不用传,长度 <= 256 byte
/// </summary>
[
JsonPropertyName
(
"ext_valid_shop_id"
)]
public
string
ExtValidShopId
{
get
;
set
;
}
/// <summary>
///开发者侧卡劵核销门店对应的抖音poiId,ext_valid_shop_id未匹配抖音POI时不用传,长度<= 128 byte
/// </summary>
[
JsonPropertyName
(
"valid_poi_id_str"
)]
public
string
ValidPoiIdStr
{
get
;
set
;
}
/// <summary>
/// 订单总价,单位为分
/// </summary>
[
JsonPropertyName
(
"ext_goods_id"
)]
public
string
ExtGoodsId
{
get
;
set
;
}
/// <summary>
/// 商品名称,长度 <= 256 byte
/// </summary>
[
JsonPropertyName
(
"goods_name"
)]
public
string
GoodsName
{
get
;
set
;
}
/// <summary>
/// 商品描述信息。向用户介绍商品,长度 <= 120byte
/// </summary>
[
JsonPropertyName
(
"goods_info"
)]
public
string
GoodsInfo
{
get
;
set
;
}
/// <summary>
/// 商品图片,完整的url地址 长度 <= 512 byte
/// </summary>
[
JsonPropertyName
(
"goods_cover_image"
)]
public
string
GoodsCoverImage
{
get
;
set
;
}
/// <summary>
/// 商品详情页的外链跳转类型,通过该接口上传的都为2 1: H5 2: 抖音小程序
/// </summary>
[
JsonPropertyName
(
"goods_entry_type"
)]
public
int
GoodsEntryType
{
get
;
set
;
}
/// <summary>
/// 商品详情页的外链跳转schema参数,格式为 JSON 字符串,长度 <= 512 byte, 详见 entry_schema 说明
/// </summary>
[
JsonPropertyName
(
"goods_entry_schema"
)]
public
string
GoodsEntrySchema
{
get
;
set
;
}
/// <summary>
/// 生效时间,yyyy-MM-dd HH:mm:ss 格式字符串,24 小时制
/// </summary>
[
JsonPropertyName
(
"start_valid_time"
)]
public
string
StartValidTime
{
get
;
set
;
}
/// <summary>
/// 失效时间,yyyy-MM-dd HH:mm:ss格式字符串,24小时制
/// </summary>
[
JsonPropertyName
(
"end_valid_time"
)]
public
string
EndValidTime
{
get
;
set
;
}
/// <summary>
/// 用户购买团购券的数量
/// </summary>
[
JsonPropertyName
(
"ticket_num"
)]
public
int
TicketNum
{
get
;
set
;
}
/// <summary>
/// 开发者侧券 ID,该信息用于用户可以明确的感知是哪一张券。格式为 JSON 数组字符串,每个 ID 长度 <= 64byte
/// </summary>
[
JsonPropertyName
(
"ext_ticket_ids"
)]
public
object
ExtTicketIds
{
get
;
set
;
}
/// <summary>
/// 券的使用说明。JSON 数组字符串,最多可以有10条,每条长度 <= 50byte。必须写明券的使用条件、领取条件、退款规则,请参考示例。
/// </summary>
[
JsonPropertyName
(
"ticket_description"
)]
public
object
TicketDescription
{
get
;
set
;
}
}
}
src/RsCode.Douyin/Payment/PrepayRequest.cs
0 → 100644
浏览文件 @
c675b8b7
/*
* 项目:抖音开放平台SDK RsCode.Douyin
* 协议:MIT License 2.0
* 作者:河南软商网络科技有限公司
* 项目己托管于
* github
https://github.com/kuiyu/RsCode.git
*/
using
System.Text.Json.Serialization
;
namespace
RsCode.Douyin.Payment
{
/// <summary>
/// 预支付
/// <see cref="https://developer.open-douyin.com/docs/resource/zh-CN/mini-app/develop/server/ecpay/pay-list/pay"/>
/// </summary>
public
class
PrepayRequest
:
DouyinRequest
{
public
PrepayRequest
(
string
appId
,
string
orderNo
,
decimal
totalAmount
,
string
notifyUrl
,
string
subject
=
"抖音商品"
)
{
AppId
=
appId
;
OutOrderNo
=
orderNo
;
TotalAmount
=
DouyinTool
.
Price
(
totalAmount
);
NotifyUrl
=
notifyUrl
;
Subject
=
subject
;
Body
=
subject
;
}
public
override
string
GetApiUrl
()
{
string
url
=
"https://developer.toutiao.com/api/apps/ecpay/v1/create_order"
;
return
url
;
}
/// <summary>
/// 小程序APPID
/// </summary>
[
JsonPropertyName
(
"app_id"
)]
public
string
AppId
{
get
;
set
;
}
/// <summary>
/// 开发者侧的订单号。 只能是数字、大小写字母_-*且在同一个app_id下唯一
/// </summary>
[
JsonPropertyName
(
"out_order_no"
)]
public
string
OutOrderNo
{
get
;
set
;
}
/// <summary>
/// 支付价格。 单位为[分]
/// </summary>
[
JsonPropertyName
(
"total_amount"
)]
public
int
TotalAmount
{
get
;
set
;
}
/// <summary>
/// 商品描述。 长度限制不超过 128 字节且不超过 42 字符
/// </summary>
[
JsonPropertyName
(
"subject"
)]
public
string
Subject
{
get
;
set
;
}
/// <summary>
/// 商品详情 长度限制不超过 128 字节且不超过 42 字符
/// </summary>
[
JsonPropertyName
(
"body"
)]
public
string
Body
{
get
;
set
;
}
/// <summary>
/// 订单过期时间(秒)。最小5分钟,最大2天,小于5分钟会被置为5分钟,大于2天会被置为2天
/// </summary>
[
JsonPropertyName
(
"valid_time"
)]
public
int
ValidTime
{
get
;
set
;
}
=
3600
*
48
;
/// <summary>
/// 签名
/// </summary>
[
JsonPropertyName
(
"sign"
)]
public
string
Sign
{
get
;
set
;
}
/// <summary>
/// 开发者自定义字段,回调原样回传。 超过最大长度会被截断
/// </summary>
[
JsonPropertyName
(
"cp_extra"
)]
public
string
CpExtra
{
get
;
set
;
}
/// <summary>
/// 商户自定义回调地址,必须以 https 开头,支持 443 端口。 指定时,支付成功后抖音会请求该地址通知开发者
/// </summary>
[
JsonPropertyName
(
"notify_url"
)]
public
string
NotifyUrl
{
get
;
set
;
}
/// <summary>
/// 第三方平台服务商 id,非服务商模式留空
/// </summary>
[
JsonPropertyName
(
"thirdparty_id"
)]
public
string
ThirdPartyId
{
get
;
set
;
}
/// <summary>
/// 可用此字段指定本单使用的收款商户号(目前为灰度功能,需要联系平台运营添加白名单,白名单添加1小时后生效;未在白名单的小程序,该字段不生效)
/// </summary>
[
JsonPropertyName
(
"store_uid"
)]
public
string
StoreUid
{
get
;
set
;
}
/// <summary>
/// 是否屏蔽支付完成后推送用户抖音消息,1-屏蔽 0-非屏蔽,默认为0。 特别注意: 若接入POI, 请传1。因为POI订单体系会发消息,所以不用再接收一次担保支付推送消息,
/// </summary>
[
JsonPropertyName
(
"disable_msg"
)]
public
int
DisableMsg
{
get
;
set
;
}
/// <summary>
/// 支付完成后推送给用户的抖音消息跳转页面,开发者需要传入在app.json中定义的链接,如果不传则跳转首页。
/// </summary>
[
JsonPropertyName
(
"msg_page"
)]
public
string
MsgPage
{
get
;
set
;
}
/// <summary>
/// 订单拓展信息,详见下面
/// expand_order_info参数说明
/// </summary>
[
JsonPropertyName
(
"expand_order_info"
)]
public
ExpandOrderInfo
ExpandOrderInfo
{
get
;
set
;
}
/// <summary>
/// 屏蔽指定支付方式,屏蔽多个支付方式,请使用逗号","分割,枚举值:
/// 屏蔽微信支付:LIMIT_WX
/// 屏蔽支付宝支付:LIMIT_ALI
/// 屏蔽抖音支付:LIMIT_DYZF
/// 特殊说明:若之前开通了白名单,平台会保留之前屏蔽逻辑;若传入该参数,会优先以传入的为准,白名单则无效
/// </summary>
[
JsonPropertyName
(
"limit_pay_way"
)]
public
string
LimitPayWay
{
get
;
set
;
}
}
public
class
ExpandOrderInfo
{
/// <summary>
/// 配送费原价,单位为[分],仅外卖小程序需要传对应信息
/// </summary>
[
JsonPropertyName
(
"original_delivery_fee"
)]
public
int
OriginalDeliveryFee
{
get
;
set
;
}
/// <summary>
/// 实付配送费,单位为[分],仅外卖小程序需要传对应信息
/// </summary>
[
JsonPropertyName
(
"actual_delivery_fee"
)]
public
int
ActualDeliveryFee
{
get
;
set
;
}
}
}
src/RsCode.Douyin/Payment/PrepayResponse.cs
0 → 100644
浏览文件 @
c675b8b7
/*
* 项目:抖音开放平台SDK RsCode.Douyin
* 协议:MIT License 2.0
* 作者:河南软商网络科技有限公司
* 项目己托管于
* github
https://github.com/kuiyu/RsCode.git
*/
using
System.Text.Json.Serialization
;
namespace
RsCode.Douyin.Payment
{
public
class
PrepayResponse
:
DouyinResponse
{
/// <summary>
/// 返回码,详见错误码
/// </summary>
[
JsonPropertyName
(
"err_no"
)]
public
int
ErrNo
{
get
;
set
;
}
/// <summary>
/// 返回码描述,详见错误码描述
/// </summary>
[
JsonPropertyName
(
"err_tips"
)]
public
string
ErrTips
{
get
;
set
;
}
[
JsonPropertyName
(
"data"
)]
public
PrepayResult
Data
{
get
;
set
;
}
}
public
class
PrepayResult
{
/// <summary>
/// 抖音侧唯一订单号
/// </summary>
[
JsonPropertyName
(
"order_id"
)]
public
string
OrderId
{
get
;
set
;
}
/// <summary>
/// 签名后的订单信息
/// </summary>
[
JsonPropertyName
(
"order_token"
)]
public
string
OrderToken
{
get
;
set
;
}
}
}
src/RsCode.Douyin/Payment/RefundNotifyData.cs
0 → 100644
浏览文件 @
c675b8b7
/*
* 项目:抖音开放平台SDK RsCode.Douyin
* 协议:MIT License 2.0
* 作者:河南软商网络科技有限公司
* 项目己托管于
* github
https://github.com/kuiyu/RsCode.git
*/
using
System.Text.Json.Serialization
;
namespace
RsCode.Douyin.Payment
{
public
class
RefundNotifyData
{
/// <summary>
/// Unix 时间戳,字符串类型
/// </summary>
[
JsonPropertyName
(
"timestamp"
)]
public
string
Timestamp
{
get
;
set
;
}
/// <summary>
/// 随机数
/// </summary>
[
JsonPropertyName
(
"nonce"
)]
public
string
Nonce
{
get
;
set
;
}
/// <summary>
/// 订单信息的 json 字符串
/// 对应RefundNotifyResult
/// </summary>
[
JsonPropertyName
(
"msg"
)]
public
string
Msg
{
get
;
set
;
}
/// <summary>
/// 签名
/// </summary>
[
JsonPropertyName
(
"msg_signature"
)]
public
string
MsgSignature
{
get
;
set
;
}
/// <summary>
/// 回调类型标记,支付成功回调为"payment"
/// </summary>
[
JsonPropertyName
(
"type"
)]
public
string
Type
{
get
;
set
;
}
}
}
src/RsCode.Douyin/Payment/RefundNotifyResult.cs
0 → 100644
浏览文件 @
c675b8b7
/*
* 项目:抖音开放平台SDK RsCode.Douyin
* 协议:MIT License 2.0
* 作者:河南软商网络科技有限公司
* 项目己托管于
* github
https://github.com/kuiyu/RsCode.git
*/
using
System.Text.Json.Serialization
;
namespace
RsCode.Douyin.Payment
{
public
class
RefundNotifyResult
{
/// <summary>
/// 当前交易发起的小程序id
/// </summary>
[
JsonPropertyName
(
"appid"
)]
public
string
AppId
{
get
;
set
;
}
/// <summary>
///开发者侧的退款订单号
/// </summary>
[
JsonPropertyName
(
"cp_refundno"
)]
public
string
CpRefundNo
{
get
;
set
;
}
/// <summary>
/// 预下单时开发者传入字段
/// </summary>
[
JsonPropertyName
(
"cp_extra"
)]
public
string
CpExtra
{
get
;
set
;
}
/// <summary>
/// 状态枚举值:
/// SUCCESS:成功 FAIL:失败
/// </summary>
[
JsonPropertyName
(
"refund_status"
)]
public
string
RefundStatus
{
get
;
set
;
}
/// <summary>
/// 退款金额,单位为分
/// </summary>
[
JsonPropertyName
(
"refund_amount"
)]
public
int
RefundAmount
{
get
;
set
;
}
/// <summary>
///退款时间, 格式为"yyyy-MM-dd hh:mm:ss"
/// </summary>
[
JsonPropertyName
(
"refunded_at"
)]
public
string
RefundAt
{
get
;
set
;
}
/// <summary>
/// 退款失败原因描述,详见发起退款错误码
/// </summary>
[
JsonPropertyName
(
"message"
)]
public
string
Message
{
get
;
set
;
}
/// <summary>
/// 抖音侧订单号
/// </summary>
[
JsonPropertyName
(
"order_id"
)]
public
string
OrderId
{
get
;
set
;
}
/// <summary>
/// 抖音退款单号
/// </summary>
[
JsonPropertyName
(
"refund_no"
)]
public
string
RefundNo
{
get
;
set
;
}
/// <summary>
/// 是否为分账后退款
/// </summary>
[
JsonPropertyName
(
"is_all_settled"
)]
public
bool
IsAllSettled
{
get
;
set
;
}
}
}
src/RsCode.Douyin/Payment/RefundQueryRequest.cs
0 → 100644
浏览文件 @
c675b8b7
/*
* 项目:抖音开放平台SDK RsCode.Douyin
* 协议:MIT License 2.0
* 作者:河南软商网络科技有限公司
* 项目己托管于
* github
https://github.com/kuiyu/RsCode.git
*/
using
System
;
using
System.Collections.Generic
;
using
System.Linq
;
using
System.Text
;
using
System.Text.Json.Serialization
;
using
System.Threading.Tasks
;
namespace
RsCode.Douyin.Payment
{
public
class
RefundQueryRequest
:
DouyinRequest
{
public
override
string
GetApiUrl
()
{
return
"https://developer.toutiao.com/api/apps/ecpay/v1/query_refund"
;
}
/// <summary>
/// 小程序APPID
/// </summary>
[
JsonPropertyName
(
"app_id"
)]
public
string
AppId
{
get
;
set
;
}
/// <summary>
/// 商户退款单号
/// </summary>
[
JsonPropertyName
(
"out_refund_no"
)]
public
string
OutRefundNo
{
get
;
set
;
}
/// <summary>
/// 签名
/// </summary>
[
JsonPropertyName
(
"sign"
)]
public
string
Sign
{
get
;
set
;
}
/// <summary>
/// 第三方平台服务商 id,非服务商模式留空
/// </summary>
[
JsonPropertyName
(
"thirdparty_id"
)]
public
string
ThirdPartyId
{
get
;
set
;
}
}
}
src/RsCode.Douyin/Payment/RefundQueryResponse.cs
0 → 100644
浏览文件 @
c675b8b7
/*
* 项目:抖音开放平台SDK RsCode.Douyin
* 协议:MIT License 2.0
* 作者:河南软商网络科技有限公司
* 项目己托管于
* github
https://github.com/kuiyu/RsCode.git
*/
using
System.Text.Json.Serialization
;
namespace
RsCode.Douyin.Payment
{
public
class
RefundQueryResponse
:
DouyinResponse
{
/// <summary>
/// 错误码
/// </summary>
[
JsonPropertyName
(
"err_no"
)]
public
long
ErrNo
{
get
;
set
;
}
/// <summary>
/// 错误信息
/// </summary>
[
JsonPropertyName
(
"err_tips"
)]
public
string
ErrTips
{
get
;
set
;
}
/// <summary>
///
/// </summary>
[
JsonPropertyName
(
"refundInfo"
)]
public
RefundResult
RefundInfo
{
get
;
set
;
}
}
public
class
RefundResult
{
/// <summary>
/// 退款金额,单位为分
/// </summary>
[
JsonPropertyName
(
"refund_amount"
)]
public
int
RefundAmount
{
get
;
set
;
}
/// <summary>
/// 退款状态枚举值:
/// SUCCESS:成功 PROCESSING:处理中 FAIL:失败
/// </summary>
[
JsonPropertyName
(
"refund_status"
)]
public
string
RefundStatus
{
get
;
set
;
}
/// <summary>
///退款时间, 格式为"yyyy-MM-dd hh:mm:ss"
/// </summary>
[
JsonPropertyName
(
"refunded_at"
)]
public
string
RefundAt
{
get
;
set
;
}
/// <summary>
/// 退款渠道,TRUE:分账后退款,现金户出款
/// FALSE:分账前退款,在途户出款
/// </summary>
[
JsonPropertyName
(
"is_all_settled"
)]
public
bool
IsAllSettled
{
get
;
set
;
}
/// <summary>
/// 抖音退款单号
/// </summary>
[
JsonPropertyName
(
"refund_no"
)]
public
string
RefundNo
{
get
;
set
;
}
/// <summary>
/// 开发者自定义字段,回调原样回传
/// </summary>
[
JsonPropertyName
(
"cp_extra"
)]
public
string
CpExtra
{
get
;
set
;
}
/// <summary>
/// 退款错误描述
/// </summary>
[
JsonPropertyName
(
"msg"
)]
public
string
Msg
{
get
;
set
;
}
}
}
src/RsCode.Douyin/Payment/RefundRequest.cs
0 → 100644
浏览文件 @
c675b8b7
/*
* 项目:抖音开放平台SDK RsCode.Douyin
* 协议:MIT License 2.0
* 作者:河南软商网络科技有限公司
* 项目己托管于
* github
https://github.com/kuiyu/RsCode.git
*/
using
System.Text.Json.Serialization
;
namespace
RsCode.Douyin.Payment
{
/// <summary>
/// <see cref="https://developer.open-douyin.com/docs/resource/zh-CN/mini-app/develop/server/ecpay/refund-list/refund"/>
/// </summary>
public
class
RefundRequest
:
DouyinRequest
{
public
override
string
GetApiUrl
()
{
return
"https://developer.toutiao.com/api/apps/ecpay/v1/create_refund"
;
}
/// <summary>
/// 小程序APPID
/// </summary>
[
JsonPropertyName
(
"app_id"
)]
public
string
AppId
{
get
;
set
;
}
/// <summary>
/// 商户分配支付单号,标识进行退款的订单
/// </summary>
[
JsonPropertyName
(
"out_order_no"
)]
public
string
OutOrderNo
{
get
;
set
;
}
/// <summary>
/// 商户分配退款号,保证在商户中唯一
/// </summary>
[
JsonPropertyName
(
"out_refund_no"
)]
public
string
OutRefundNo
{
get
;
set
;
}
/// <summary>
///退款原因
/// </summary>
[
JsonPropertyName
(
"reason"
)]
public
string
Reason
{
get
;
set
;
}
/// <summary>
/// 退款金额,单位分
/// </summary>
[
JsonPropertyName
(
"refund_amount"
)]
public
int
RefundAmount
{
get
;
set
;
}
/// <summary>
/// 签名
/// </summary>
[
JsonPropertyName
(
"sign"
)]
public
string
Sign
{
get
;
set
;
}
/// <summary>
/// 开发者自定义字段,回调原样回传。 超过最大长度会被截断
/// </summary>
[
JsonPropertyName
(
"cp_extra"
)]
public
string
CpExtra
{
get
;
set
;
}
/// <summary>
/// 商户自定义回调地址,必须以 https 开头,支持 443 端口
/// </summary>
[
JsonPropertyName
(
"notify_url"
)]
public
string
NotifyUrl
{
get
;
set
;
}
/// <summary>
/// 第三方平台服务商id,服务商模式接入必传,非服务商模式留空
/// </summary>
[
JsonPropertyName
(
"thirdparty_id"
)]
public
string
ThirdPartyId
{
get
;
set
;
}
/// <summary>
/// 是否屏蔽支付完成后推送用户抖音消息,1-屏蔽 0-非屏蔽,默认为0。 特别注意: 若接入POI, 请传1。因为POI订单体系会发消息,所以不用再接收一次担保支付推送消息,请传1
/// </summary>
[
JsonPropertyName
(
"disable_msg"
)]
public
int
DisableMsg
{
get
;
set
;
}
/// <summary>
/// 退款完成后推送给用户的抖音消息跳转页面,开发者需要传入在app.json中定义的链接,如果不传则跳转首页。
/// </summary>
[
JsonPropertyName
(
"msg_page"
)]
public
string
MsgPage
{
get
;
set
;
}
}
}
src/RsCode.Douyin/Payment/RefundResponse.cs
0 → 100644
浏览文件 @
c675b8b7
/*
* 项目:抖音开放平台SDK RsCode.Douyin
* 协议:MIT License 2.0
* 作者:河南软商网络科技有限公司
* 项目己托管于
* github
https://github.com/kuiyu/RsCode.git
*/
using
System
;
using
System.Collections.Generic
;
using
System.Linq
;
using
System.Text
;
using
System.Text.Json.Serialization
;
using
System.Threading.Tasks
;
namespace
RsCode.Douyin.Payment
{
public
class
RefundResponse
:
DouyinResponse
{
/// <summary>
/// 错误码
/// </summary>
[
JsonPropertyName
(
"err_no"
)]
public
long
ErrNo
{
get
;
set
;
}
/// <summary>
/// 错误信息
/// </summary>
[
JsonPropertyName
(
"err_tips"
)]
public
string
ErrTips
{
get
;
set
;
}
/// <summary>
/// 担保交易服务端退款单号
/// </summary>
[
JsonPropertyName
(
"refund_no"
)]
public
string
RefundNo
{
get
;
set
;
}
}
}
src/RsCode.Douyin/RsCode.Douyin.csproj
0 → 100644
浏览文件 @
c675b8b7
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<PackageProjectUrl>https://rscode.cn</PackageProjectUrl>
<PackageIcon>rscode64_64.png</PackageIcon>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\RsCode.AspNetCore\RsCode.AspNetCore.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="rscode64_64.png">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
</ItemGroup>
<ItemGroup>
<Folder Include="Mount\" />
</ItemGroup>
</Project>
src/RsCode.Douyin/Storage/RemoveUserStorageRequest.cs
0 → 100644
浏览文件 @
c675b8b7
/*
* 项目:抖音开放平台SDK RsCode.Douyin
* 协议:MIT License 2.0
* 作者:河南软商网络科技有限公司
* 项目己托管于
* github
https://github.com/kuiyu/RsCode.git
*/
using
System.Text.Json.Serialization
;
namespace
RsCode.Douyin.Storage
{
/// <summary>
/// 删除存储到字节跳动的云存储服务的 key-value 数据。当开发者不需要该用户信息时,需要删除,以免占用过大的存储空间。
/// <see cref="https://developer.open-douyin.com/docs/resource/zh-CN/mini-app/develop/server/data-caching/remove-user-storage"/>
/// </summary>
public
class
RemoveUserStorageRequest
:
DouyinRequest
{
/// <summary>
/// 服务端 API 调用标识
/// <see cref="https://developer.open-douyin.com/docs/resource/zh-CN/mini-app/develop/server/interface-request-credential/get-access-token/"/>
/// </summary>
[
JsonPropertyName
(
"access_token"
)]
public
string
AccessToken
{
get
;
set
;
}
/// <summary>
/// 登录用户唯一标识
/// </summary>
[
JsonPropertyName
(
"openid"
)]
public
string
OpenId
{
get
;
set
;
}
/// <summary>
/// 用户登录态签名,参考用户登录态签名算法
/// <see cref="https://developer.open-douyin.com/docs/resource/zh-CN/mini-app/develop/server/other/user-login-sign/"/>
/// </summary>
[
JsonPropertyName
(
"signature"
)]
public
string
Signature
{
get
;
set
;
}
/// <summary>
/// 用户登录态签名的编码方法
/// <see cref="https://developer.open-douyin.com/docs/resource/zh-CN/mini-app/develop/server/other/user-login-sign/"/>
/// </summary>
[
JsonPropertyName
(
"sig_method"
)]
public
string
SigMethod
{
get
;
set
;
}
/// <summary>
/// (body 中) 要删除的用户数据的 key list
/// </summary>
[
JsonPropertyName
(
"key"
)]
public
string
[]
Key
{
get
;
set
;
}
public
override
string
GetApiUrl
()
{
return
"https://developer.toutiao.com/api/apps/remove_user_storage"
;
}
}
}
src/RsCode.Douyin/Storage/RemoveUserStorageResponse.cs
0 → 100644
浏览文件 @
c675b8b7
/*
* 项目:抖音开放平台SDK RsCode.Douyin
* 协议:MIT License 2.0
* 作者:河南软商网络科技有限公司
* 项目己托管于
* github
https://github.com/kuiyu/RsCode.git
*/
using
System
;
using
System.Collections.Generic
;
using
System.Linq
;
using
System.Text
;
using
System.Text.Json.Serialization
;
using
System.Threading.Tasks
;
namespace
RsCode.Douyin.Storage
{
public
class
RemoveUserStorageResponse
:
DouyinResponse
{
}
}
src/RsCode.Douyin/Storage/SetUserStorageReqeust.cs
0 → 100644
浏览文件 @
c675b8b7
/*
* 项目:抖音开放平台SDK RsCode.Douyin
* 协议:MIT License 2.0
* 作者:河南软商网络科技有限公司
* 项目己托管于
* github
https://github.com/kuiyu/RsCode.git
*/
using
System
;
using
System.Collections.Generic
;
using
System.Linq
;
using
System.Text
;
using
System.Text.Json.Serialization
;
using
System.Threading.Tasks
;
namespace
RsCode.Douyin.Storage
{
/// <summary>
/// 以 key-value 形式存储用户数据到小程序平台的云存储服务。若开发者无内部存储服务则可接入,免费且无需申请。一般情况下只存储用户的基本信息,禁止写入大量不相干信息。
/// <see cref="https://developer.open-douyin.com/docs/resource/zh-CN/mini-app/develop/server/data-caching/set-user-storage"/>
/// </summary>
public
class
SetUserStorageReqeust
:
DouyinRequest
{
/// <summary>
/// 服务端 API 调用标识
/// </summary>
[
JsonPropertyName
(
"access_token"
)]
public
string
SessionKey
{
get
;
set
;
}
/// <summary>
/// 登录用户唯一标识
/// <see cref="https://developer.open-douyin.com/docs/resource/zh-CN/mini-app/develop/server/log-in/code-2-session/"/>
/// </summary>
[
JsonPropertyName
(
"openid"
)]
public
string
OpenId
{
get
;
set
;
}
/// <summary>
/// 用户登录态签名
/// <see cref="https://developer.open-douyin.com/docs/resource/zh-CN/mini-app/develop/server/other/user-login-sign/"/>
/// </summary>
[
JsonPropertyName
(
"signature"
)]
public
string
Signature
{
get
;
set
;
}
/// <summary>
/// 用户登录态签名的编码方法,参考用户登录态签名算法
/// <see cref="https://developer.open-douyin.com/docs/resource/zh-CN/mini-app/develop/server/other/user-login-sign/"/>
/// </summary>
[
JsonPropertyName
(
"sig_method"
)]
public
string
SigMethod
{
get
;
set
;
}
/// <summary>
/// (body 中) 要设置的用户数据
/// </summary>
[
JsonPropertyName
(
"kv_list"
)]
public
Object
[]
KvList
{
get
;
set
;
}
}
}
src/RsCode.Douyin/Storage/SetUserStorageResponse.cs
0 → 100644
浏览文件 @
c675b8b7
/*
* 项目:抖音开放平台SDK RsCode.Douyin
* 协议:MIT License 2.0
* 作者:河南软商网络科技有限公司
* 项目己托管于
* github
https://github.com/kuiyu/RsCode.git
*/
using
System
;
using
System.Collections.Generic
;
using
System.Linq
;
using
System.Text
;
using
System.Text.Json.Serialization
;
using
System.Threading.Tasks
;
namespace
RsCode.Douyin.Storage
{
public
class
SetUserStorageResponse
:
DouyinResponse
{
/// <summary>
/// 会话密钥,如果请求时有 code 参数才会返回
/// </summary>
[
JsonPropertyName
(
"session_key"
)]
public
string
SessionKey
{
get
;
set
;
}
/// <summary>
/// 用户在当前小程序的 ID,如果请求时有 code 参数才会返回
/// </summary>
[
JsonPropertyName
(
"openid"
)]
public
string
OpenId
{
get
;
set
;
}
/// <summary>
/// 匿名用户在当前小程序的 ID,如果请求时有 anonymous_code 参数才会返回
/// </summary>
[
JsonPropertyName
(
"anonymous_openid"
)]
public
string
AnonymousOpenId
{
get
;
set
;
}
/// <summary>
/// 用户在小程序平台的唯一标识符,请求时有 code 参数才会返回。如果开发者拥有多个小程序,可通过 unionid 来区分用户的唯一性。
/// </summary>
[
JsonPropertyName
(
"unionid"
)]
public
string
UnionId
{
get
;
set
;
}
}
}
src/RsCode.Douyin/Usings.cs
0 → 100644
浏览文件 @
c675b8b7
global
using
RsCode.Douyin.Core
;
global
using
RsCode.Douyin.OAuth
;
\ No newline at end of file
src/RsCode.Douyin/rscode64_64.png
0 → 100644
浏览文件 @
c675b8b7
5.6 KB
src/RsCode.Storage.Aliyun/AliyunOssService.cs
浏览文件 @
c675b8b7
...
...
@@ -28,7 +28,7 @@ namespace RsCode.Storage.Aliyun
AccessKeySecret
=
optionsSnapshot
.
Value
.
AccessKeySecret
;
}
public
async
Task
<
AlibabaCloud
.
SDK
.
Sts20150401
.
Models
.
AssumeRoleResponseBody
>
GetStsTokenAsync
(
string
sessionName
,
string
endpointName
,
string
roleArn
=
""
,
int
durationSeconds
=
9
00
)
public
async
Task
<
AlibabaCloud
.
SDK
.
Sts20150401
.
Models
.
AssumeRoleResponseBody
>
GetStsTokenAsync
(
string
sessionName
,
string
endpointName
,
string
roleArn
=
""
,
int
durationSeconds
=
36
00
)
{
try
{
...
...
@@ -176,13 +176,13 @@ namespace RsCode.Storage.Aliyun
return
await
Task
.
Run
(()=>
client
.
DeleteObject
(
bucketName
,
key
));
}
public
Uri
GeneratePresignedUri
(
string
bucketName
,
string
key
,
string
endpointName
,
AssumeRoleResponseBodyCredentials
stsToken
)
public
Uri
GeneratePresignedUri
(
string
bucketName
,
string
key
,
string
endpointName
,
AssumeRoleResponseBodyCredentials
stsToken
,
int
minute
=
60
)
{
var
endpoint
=
Options
.
GetEndPoint
(
endpointName
);
var
client
=
GetOssClient
(
stsToken
.
AccessKeyId
,
stsToken
.
AccessKeySecret
,
endpoint
.
Oss
,
stsToken
.
SecurityToken
);
//创建临时访问的url
return
client
.
GeneratePresignedUri
(
bucketName
,
key
,
DateTime
.
Now
.
AddMinutes
(
5
),
SignHttpMethod
.
Get
);
return
client
.
GeneratePresignedUri
(
bucketName
,
key
,
DateTime
.
Now
.
AddMinutes
(
minute
),
SignHttpMethod
.
Get
);
}
...
...
src/RsCode.Storage.Aliyun/IAliyunOssService.cs
浏览文件 @
c675b8b7
...
...
@@ -9,7 +9,7 @@ namespace RsCode.Storage.Aliyun
{
public
interface
IAliyunOssService
{
Task
<
AlibabaCloud
.
SDK
.
Sts20150401
.
Models
.
AssumeRoleResponseBody
>
GetStsTokenAsync
(
string
sessionName
,
string
endpointName
,
string
roleArn
=
""
,
int
durationSeconds
=
9
00
);
Task
<
AlibabaCloud
.
SDK
.
Sts20150401
.
Models
.
AssumeRoleResponseBody
>
GetStsTokenAsync
(
string
sessionName
,
string
endpointName
,
string
roleArn
=
""
,
int
durationSeconds
=
36
00
);
AlibabaCloud
.
SDK
.
Sts20150401
.
Client
CreateStsClient
(
string
accessKeyId
,
string
accessKeySecret
,
string
endpoint
);
...
...
@@ -24,7 +24,7 @@ namespace RsCode.Storage.Aliyun
/// <param name="endpointName"></param>
/// <param name="stsToken"></param>
/// <returns></returns>
Uri
GeneratePresignedUri
(
string
bucketName
,
string
key
,
string
endpointName
,
AssumeRoleResponseBodyCredentials
stsToken
);
Uri
GeneratePresignedUri
(
string
bucketName
,
string
key
,
string
endpointName
,
AssumeRoleResponseBodyCredentials
stsToken
,
int
minute
=
60
);
Task
<
DeleteObjectResult
>
DeleteFileAsync
(
string
bucketName
,
string
key
,
string
endpointName
,
AssumeRoleResponseBodyCredentials
stsToken
);
/// <summary>
/// 查询指定key对应UrL
...
...
src/RsCode/Log/LogHelper.cs
浏览文件 @
c675b8b7
...
...
@@ -20,6 +20,7 @@ using log4net;
using
System
;
using
System.ComponentModel
;
using
System.Linq
;
using
System.Runtime.CompilerServices
;
namespace
RsCode
{
...
...
@@ -62,10 +63,10 @@ namespace RsCode
/// <param name="log4Level">记录日志等级,枚举</param>
public
static
void
WriteLog
(
Type
type
,
string
logContent
,
Log4NetLevel
log4Level
,
Exception
ex
=
null
)
{
//log4net.LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType)
var
repositories
=
LogManager
.
GetAllRepositories
();
var
repository
=
repositories
.
FirstOrDefault
();
if
(
repository
==
null
)
return
;
ILog
log
=
type
==
null
?
LogManager
.
GetLogger
(
repository
.
Name
,
""
)
:
LogManager
.
GetLogger
(
type
);
...
...
tests/RsCode.Douyin.Tests/AccessTokenTest.cs
0 → 100644
浏览文件 @
c675b8b7
namespace
RsCode.Douyin.Tests
{
public
class
AccessTokenTest
{
IDouyinClient
douyinClient
;
public
AccessTokenTest
(
IDouyinClient
douyinClient
)
{
this
.
douyinClient
=
douyinClient
;
}
[
Fact
]
public
async
Task
GetAccessTokenTest
()
{
var
options
=
douyinClient
.
UseAppId
(
"tt6be31d5ef4dfda0b01"
);
var
ret
=
await
douyinClient
.
SendAsync
<
AccessTokenResponse
>(
new
AccessTokenRequest
{
AppId
=
options
.
AppId
,
AppSecret
=
options
.
Secret
});
Assert
.
Equal
(
0
,
ret
.
ErrNo
);
}
}
}
\ No newline at end of file
tests/RsCode.Douyin.Tests/RsCode.Douyin.Tests.csproj
0 → 100644
浏览文件 @
c675b8b7
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AspectCore.Extensions.Hosting" Version="2.3.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="Xunit.DependencyInjection" Version="8.7.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="3.1.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\RsCode.Douyin\RsCode.Douyin.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="appsettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="cert\douyin\private.key">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="cert\douyin\public.key">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="douyin.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
tests/RsCode.Douyin.Tests/SignTest.cs
0 → 100644
浏览文件 @
c675b8b7
using
RsCode.Douyin.Payment
;
using
System
;
using
System.Collections.Generic
;
using
System.Linq
;
using
System.Text
;
using
System.Text.Json
;
using
System.Threading.Tasks
;
namespace
RsCode.Douyin.Tests
{
public
class
SignTest
{
IDouyinClient
douyinClient
;
public
SignTest
(
IDouyinClient
douyinClient
)
{
this
.
douyinClient
=
douyinClient
;
}
[
Fact
]
public
void
CheckSignTest
()
{
string
s
=
"POST\n/abc\n1680835692\ngjjRNfQlzoDIJtVDOfUe\n{\"eventTime\":1677653869000,\"status\":102}\n"
;
string
exp
=
"RFQ65hHlo4xyZ6EC31LZC0SzsyN0nd2Fb2wAiISvY1mkiC6G8gn2QZwLGq7qgjenRGl/Z8OrTtkBHWb9GOazkJFkHrPeRqogqnwZ+kSOxGvtou8FPN669E1wwb+BShN4pIUgPFzaukR9/rCRBsbLoq9RPVA2sbf3iKoHGa81zhXjQSuFbF1CyiWkL5qqniNTM/BSfwfLZfPW8nBanRl3U+mQaymbj0DCF0ZdWhFz1FnZPAfEpx8YEwFNZWtxzz4p3WJ1swnUocJC4LXoDazo6DhEPDuoZXOXrB1SqzL1wRqA4p8uj3Z8Seki/PMGWiGpGWPMv3tJyvWmzMOuVJtEjg=="
;
var
options
=
douyinClient
.
UseAppId
(
"tt6be31d5ef4dfda0b01"
);
string
ret
=
DouyinTool
.
Sign
(
s
,
options
);
Assert
.
Equal
(
exp
,
ret
);
}
[
Fact
]
public
void
ParamSign
()
{
var
options
=
douyinClient
.
UseAppId
(
"tt6be31d5ef4dfda0b01"
);
PrepayRequest
request
=
new
PrepayRequest
(
options
.
AppId
,
"abdeiekd"
,
18
,
"http://rscode.cn"
);
var
json
=
JsonSerializer
.
Serialize
(
request
);
var
sign
=
request
.
CreateSign
(
options
);
Assert
.
Equal
(
"61a3191ee426dc64f6f409052a0653b7"
,
sign
);
}
}
}
tests/RsCode.Douyin.Tests/Startup.cs
0 → 100644
浏览文件 @
c675b8b7
using
Microsoft.Extensions.Configuration
;
using
Microsoft.Extensions.DependencyInjection
;
using
Microsoft.Extensions.Hosting
;
using
RsCode.AspNetCore
;
using
System
;
using
System.Collections.Generic
;
using
System.Linq
;
using
System.Text
;
using
System.Threading.Tasks
;
namespace
RsCode.Douyin.Tests
{
public
class
Startup
{
// public IHostBuilder CreateHostBuilder([AssemblyName assemblyName]) { }
// 自定义 host 构建
public
void
ConfigureHost
(
IHostBuilder
hostBuilder
)
{
hostBuilder
.
ConfigureAppConfiguration
(
builder
=>
{
// 注册配置
builder
.
SetBasePath
(
Directory
.
GetCurrentDirectory
())
.
AddJsonFile
(
"appsettings.json"
,
true
,
true
);
})
.
ConfigureServices
((
context
,
services
)
=>
{
// 注册自定义服务
services
.
AddRsCode
();
services
.
AddDouyin
();
})
// .UseServiceContext(o=>
//{
// var p = Predicates.ForNameSpace("RsCode.Storage.QiniuStorage.Core");
// o.AddDataAnnotations(p);
//})
;
}
// 支持的形式:
// ConfigureServices(IServiceCollection services)
// ConfigureServices(IServiceCollection services, HostBuilderContext hostBuilderContext)
// ConfigureServices(HostBuilderContext hostBuilderContext, IServiceCollection services)
public
void
ConfigureServices
(
IServiceCollection
services
,
HostBuilderContext
hostBuilderContext
)
{
//services.TryAddSingleton<CustomService>();
}
// 可以添加要用到的方法参数,会自动从注册的服务中获取服务实例,类似于 asp.net core 里 Configure 方法
public
void
Configure
(
IServiceProvider
applicationServices
)
{
// 有一些测试数据要初始化可以放在这里
// InitData();
}
}
}
tests/RsCode.Douyin.Tests/Usings.cs
0 → 100644
浏览文件 @
c675b8b7
global
using
Xunit
;
global
using
RsCode.Douyin.Core
;
global
using
RsCode.Douyin.OAuth
;
\ No newline at end of file
tests/RsCode.Douyin.Tests/log4net.config
0 → 100644
浏览文件 @
c675b8b7
<?
xml
version
=
"1.0"
encoding
=
"utf-8"
?>
<
log4net
>
<!--数据日志-->
<
appender
name
=
"InfoRollingFileAppender"
type
=
"log4net.Appender.RollingFileAppender"
>
<
lockingModel
type
=
"log4net.Appender.FileAppender+MinimalLock"
/>
<
param
name
=
"File"
value
=
"Log\Info\"
/>
<
param
name
=
"AppendToFile"
value
=
"true"
/>
<
param
name
=
"rollingStyle"
value
=
"Date"
/>
<
param
name
=
"datePattern"
value
=
"yyyy-MM-dd.'Info.log'"
/>
<
param
name
=
"staticLogFileName"
value
=
"false"
/>
<
layout
type
=
"log4net.Layout.PatternLayout"
>
<
conversionPattern
value
=
"%n%n%n 记录时间:%date %n日志级别: %-5level %n类型:%logger %n日志描述:%message %newline%newline"
/>
</
layout
>
<
filter
type
=
"log4net.Filter.LevelRangeFilter"
>
<
param
name
=
"LevelMin"
value
=
"INFO"
/>
<
param
name
=
"LevelMax"
value
=
"INFO"
/>
</
filter
>
</
appender
>
<!--错误日志-->
<
appender
name
=
"ErrorRollingFileAppender"
type
=
"log4net.Appender.RollingFileAppender"
>
<
lockingModel
type
=
"log4net.Appender.FileAppender+MinimalLock"
/>
<
param
name
=
"File"
value
=
"Log\Error\"
/>
<
param
name
=
"AppendToFile"
value
=
"true"
/>
<
param
name
=
"rollingStyle"
value
=
"Date"
/>
<
param
name
=
"datePattern"
value
=
"yyyy-MM-dd.'error.log'"
/>
<
param
name
=
"staticLogFileName"
value
=
"false"
/>
<
layout
type
=
"log4net.Layout.PatternLayout"
>
<
conversionPattern
value
=
"%n%n%n 记录时间:%date %n日志级别: %-5level %n出错类:%logger %n错误信息:%message %newline%newline"
/>
</
layout
>
<
filter
type
=
"log4net.Filter.LevelRangeFilter"
>
<
param
name
=
"LevelMin"
value
=
"ERROR"
/>
<
param
name
=
"LevelMax"
value
=
"ERROR"
/>
</
filter
>
</
appender
>
<!--调试日志-->
<
appender
name
=
"DebugRollingFileAppender"
type
=
"log4net.Appender.RollingFileAppender"
>
<
lockingModel
type
=
"log4net.Appender.FileAppender+MinimalLock"
/>
<
param
name
=
"File"
value
=
"Log\Debug\"
/>
<
param
name
=
"AppendToFile"
value
=
"true"
/>
<
param
name
=
"rollingStyle"
value
=
"Date"
/>
<
param
name
=
"datePattern"
value
=
"yyyy-MM-dd.'debug.log'"
/>
<
param
name
=
"staticLogFileName"
value
=
"false"
/>
<
layout
type
=
"log4net.Layout.PatternLayout"
>
<
conversionPattern
value
=
"%n%n%n 记录时间:%date %n日志级别: %-5level %n类型:%logger %n调试信息:%message %newline%newline"
/>
</
layout
>
<
filter
type
=
"log4net.Filter.LevelRangeFilter"
>
<
param
name
=
"LevelMin"
value
=
"DEBUG"
/>
<
param
name
=
"LevelMax"
value
=
"DEBUG"
/>
</
filter
>
</
appender
>
<
root
>
<
level
value
=
"DEBUG"
/>
<!--文件形式记录日志-->
<
appender
-
ref
ref
=
"ErrorRollingFileAppender"
/>
<
appender
-
ref
ref
=
"DebugRollingFileAppender"
/>
<
appender
-
ref
ref
=
"InfoRollingFileAppender"
/>
</
root
>
</
log4net
>
\ No newline at end of file
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录