fix(track): UpsertSmallBag auth params in query, align sign helper
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
545f0825f8
commit
e642f089ba
@ -24,9 +24,6 @@ $timestamp = [DateTimeOffset]::UtcNow.ToUnixTimeSeconds()
|
|||||||
$sign = Get-SmallBagSign -Key $AppKey -Ts $timestamp -Secret $AppSecret
|
$sign = Get-SmallBagSign -Key $AppKey -Ts $timestamp -Secret $AppSecret
|
||||||
|
|
||||||
$body = @{
|
$body = @{
|
||||||
appKey = $AppKey
|
|
||||||
timestamp = $timestamp
|
|
||||||
sign = $sign
|
|
||||||
client = $Client
|
client = $Client
|
||||||
orderNo = $OrderNo
|
orderNo = $OrderNo
|
||||||
trackNo = ""
|
trackNo = ""
|
||||||
@ -43,7 +40,8 @@ $body = @{
|
|||||||
}
|
}
|
||||||
} | ConvertTo-Json -Depth 5 -Compress
|
} | ConvertTo-Json -Depth 5 -Compress
|
||||||
|
|
||||||
$url = ($BaseUrl.TrimEnd('/')) + "/OrderV2/UpsertSmallBag"
|
$base = $BaseUrl.TrimEnd('/')
|
||||||
|
$url = "$base/OrderV2/UpsertSmallBag?appKey=$([uri]::EscapeDataString($AppKey))×tamp=$timestamp&sign=$([uri]::EscapeDataString($sign))"
|
||||||
|
|
||||||
Write-Host "========== UpsertSmallBag Debug ==========" -ForegroundColor Cyan
|
Write-Host "========== UpsertSmallBag Debug ==========" -ForegroundColor Cyan
|
||||||
Write-Host "URL: $url"
|
Write-Host "URL: $url"
|
||||||
@ -66,7 +64,7 @@ catch {
|
|||||||
$reader = New-Object System.IO.StreamReader($_.Exception.Response.GetResponseStream())
|
$reader = New-Object System.IO.StreamReader($_.Exception.Response.GetResponseStream())
|
||||||
$errBody = $reader.ReadToEnd()
|
$errBody = $reader.ReadToEnd()
|
||||||
Write-Host "HTTP $([int]$_.Exception.Response.StatusCode)" -ForegroundColor Red
|
Write-Host "HTTP $([int]$_.Exception.Response.StatusCode)" -ForegroundColor Red
|
||||||
if ($errBody) { Write-Host $errBody } else { Write-Host "(empty body — 多为签名/鉴权失败,请核对 appKey/appSecret 或联系轨迹平台)" }
|
if ($errBody) { Write-Host $errBody } else { Write-Host "(empty body — 请核对 appKey/appSecret 或联系轨迹平台)" }
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Write-Host $_.Exception.Message -ForegroundColor Red
|
Write-Host $_.Exception.Message -ForegroundColor Red
|
||||||
|
|||||||
@ -66,15 +66,15 @@ internal sealed class SmallBagTrackOrderClient(
|
|||||||
var appKey = settings.AppKey.Trim();
|
var appKey = settings.AppKey.Trim();
|
||||||
var timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
var timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||||||
var sign = SmallBagTrackSignHelper.GenerateSign(appKey, timestamp, settings.AppSecret.Trim());
|
var sign = SmallBagTrackSignHelper.GenerateSign(appKey, timestamp, settings.AppSecret.Trim());
|
||||||
var payload = SmallBagTrackUpsertApiRequest.FromBusinessRequest(request, appKey, timestamp, sign);
|
var body = SmallBagTrackUpsertApiRequest.FromBusinessRequest(request);
|
||||||
var url = $"{settings.BaseUrl.Trim().TrimEnd('/')}/OrderV2/UpsertSmallBag";
|
var url = SmallBagTrackUpsertApiRequest.BuildRequestUrl(settings.BaseUrl, appKey, timestamp, sign);
|
||||||
var requestJson = JsonSerializer.Serialize(payload, JsonOptions);
|
var requestJson = JsonSerializer.Serialize(body, JsonOptions);
|
||||||
|
|
||||||
var client = httpClientFactory.CreateClient(HttpClientName);
|
var client = httpClientFactory.CreateClient(HttpClientName);
|
||||||
client.Timeout = TimeSpan.FromSeconds(Math.Max(5, settings.TimeoutSeconds));
|
client.Timeout = TimeSpan.FromSeconds(Math.Max(5, settings.TimeoutSeconds));
|
||||||
|
|
||||||
using var response = await client.PostAsJsonAsync(url, payload, cancellationToken);
|
using var response = await client.PostAsJsonAsync(url, body, cancellationToken);
|
||||||
var body = await response.Content.ReadAsStringAsync(cancellationToken);
|
var responseBody = await response.Content.ReadAsStringAsync(cancellationToken);
|
||||||
return new SmallBagTrackUpsertResult
|
return new SmallBagTrackUpsertResult
|
||||||
{
|
{
|
||||||
Skipped = false,
|
Skipped = false,
|
||||||
@ -82,7 +82,7 @@ internal sealed class SmallBagTrackOrderClient(
|
|||||||
Url = url,
|
Url = url,
|
||||||
RequestJson = requestJson,
|
RequestJson = requestJson,
|
||||||
StatusCode = (int)response.StatusCode,
|
StatusCode = (int)response.StatusCode,
|
||||||
ResponseBody = body
|
ResponseBody = responseBody
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -4,13 +4,26 @@ using System.Text;
|
|||||||
namespace Oms.Application.Integrations.SmallBagTrack;
|
namespace Oms.Application.Integrations.SmallBagTrack;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 轨迹服务 API 签名:appKey={}&timestamp={}&appSecret={} → MD5 大写。
|
/// 轨迹服务 API 签名(与轨迹平台官方算法一致)。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static class SmallBagTrackSignHelper
|
internal static class SmallBagTrackSignHelper
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 生成 API 签名。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="appKey">应用唯一标识</param>
|
||||||
|
/// <param name="timestamp">Unix 时间戳(秒)</param>
|
||||||
|
/// <param name="appSecret">应用密钥</param>
|
||||||
|
/// <returns>MD5 签名(大写十六进制)</returns>
|
||||||
public static string GenerateSign(string appKey, long timestamp, string appSecret)
|
public static string GenerateSign(string appKey, long timestamp, string appSecret)
|
||||||
{
|
{
|
||||||
var signString = $"appKey={appKey}×tamp={timestamp}&appSecret={appSecret}";
|
// 1. 排序后拼接:appKey, timestamp
|
||||||
|
var sortedParams = $"appKey={appKey}×tamp={timestamp}";
|
||||||
|
|
||||||
|
// 2. 拼接密钥
|
||||||
|
var signString = $"{sortedParams}&appSecret={appSecret}";
|
||||||
|
|
||||||
|
// 3. MD5 大写
|
||||||
return CalculateMd5Hash(signString);
|
return CalculateMd5Hash(signString);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,16 +1,10 @@
|
|||||||
namespace Oms.Application.Integrations.SmallBagTrack;
|
namespace Oms.Application.Integrations.SmallBagTrack;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// POST /OrderV2/UpsertSmallBag 完整请求体(含公共参数 + 业务字段)。
|
/// POST /OrderV2/UpsertSmallBag 业务请求体(公共参数 appKey/timestamp/sign 走 Query)。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal sealed class SmallBagTrackUpsertApiRequest
|
internal sealed class SmallBagTrackUpsertApiRequest
|
||||||
{
|
{
|
||||||
public string AppKey { get; set; } = string.Empty;
|
|
||||||
|
|
||||||
public long Timestamp { get; set; }
|
|
||||||
|
|
||||||
public string Sign { get; set; } = string.Empty;
|
|
||||||
|
|
||||||
public string Client { get; set; } = string.Empty;
|
public string Client { get; set; } = string.Empty;
|
||||||
|
|
||||||
public string OrderNo { get; set; } = string.Empty;
|
public string OrderNo { get; set; } = string.Empty;
|
||||||
@ -27,16 +21,9 @@ internal sealed class SmallBagTrackUpsertApiRequest
|
|||||||
|
|
||||||
public SmallBagTrackConsigneeRequest Consignee { get; set; } = new();
|
public SmallBagTrackConsigneeRequest Consignee { get; set; } = new();
|
||||||
|
|
||||||
public static SmallBagTrackUpsertApiRequest FromBusinessRequest(
|
public static SmallBagTrackUpsertApiRequest FromBusinessRequest(SmallBagTrackUpsertRequest request) =>
|
||||||
SmallBagTrackUpsertRequest request,
|
|
||||||
string appKey,
|
|
||||||
long timestamp,
|
|
||||||
string sign) =>
|
|
||||||
new()
|
new()
|
||||||
{
|
{
|
||||||
AppKey = appKey,
|
|
||||||
Timestamp = timestamp,
|
|
||||||
Sign = sign,
|
|
||||||
Client = request.Client,
|
Client = request.Client,
|
||||||
OrderNo = request.OrderNo,
|
OrderNo = request.OrderNo,
|
||||||
TrackNo = request.TrackNo,
|
TrackNo = request.TrackNo,
|
||||||
@ -46,4 +33,15 @@ internal sealed class SmallBagTrackUpsertApiRequest
|
|||||||
ApiCode = request.ApiCode,
|
ApiCode = request.ApiCode,
|
||||||
Consignee = request.Consignee
|
Consignee = request.Consignee
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public static string BuildRequestUrl(string baseUrl, string appKey, long timestamp, string sign)
|
||||||
|
{
|
||||||
|
var root = $"{baseUrl.Trim().TrimEnd('/')}/OrderV2/UpsertSmallBag";
|
||||||
|
var query = string.Join(
|
||||||
|
'&',
|
||||||
|
$"appKey={Uri.EscapeDataString(appKey)}",
|
||||||
|
$"timestamp={timestamp}",
|
||||||
|
$"sign={Uri.EscapeDataString(sign)}");
|
||||||
|
return $"{root}?{query}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,15 +5,19 @@ namespace Oms.Tests;
|
|||||||
|
|
||||||
public class SmallBagTrackSignHelperTests
|
public class SmallBagTrackSignHelperTests
|
||||||
{
|
{
|
||||||
[Fact]
|
private const string AppKey = "W2606001";
|
||||||
public void GenerateSign_ShouldMatchMd5UppercaseRule()
|
private const string AppSecret = "ziK8UzlQ39m3kdL346XEBg8nljY8os52";
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(1700000000, "A744D764DEE7C925AD6750257A4DEEE7")]
|
||||||
|
[InlineData(1782705351, "6C76CF34DFB53A0686C7C97284133BB7")]
|
||||||
|
[InlineData(1782705489, "E9633AB5419AB15D95D38903E8001E2D")]
|
||||||
|
public void GenerateSign_ShouldMatchOfficialMd5UppercaseRule(long timestamp, string expected)
|
||||||
{
|
{
|
||||||
const string appKey = "W2606001";
|
var signString = $"appKey={AppKey}×tamp={timestamp}&appSecret={AppSecret}";
|
||||||
const long timestamp = 1700000000;
|
|
||||||
const string appSecret = "ziK8UzlQ39m3kdL346XEBg8nljY8os52";
|
|
||||||
|
|
||||||
var sign = SmallBagTrackSignHelper.GenerateSign(appKey, timestamp, appSecret);
|
SmallBagTrackSignHelper.GenerateSign(AppKey, timestamp, AppSecret)
|
||||||
|
.Should()
|
||||||
sign.Should().Be("A744D764DEE7C925AD6750257A4DEEE7");
|
.Be(expected, because: $"signString={signString}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user