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
|
||||
|
||||
$body = @{
|
||||
appKey = $AppKey
|
||||
timestamp = $timestamp
|
||||
sign = $sign
|
||||
client = $Client
|
||||
orderNo = $OrderNo
|
||||
trackNo = ""
|
||||
@ -43,7 +40,8 @@ $body = @{
|
||||
}
|
||||
} | 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 "URL: $url"
|
||||
@ -66,7 +64,7 @@ catch {
|
||||
$reader = New-Object System.IO.StreamReader($_.Exception.Response.GetResponseStream())
|
||||
$errBody = $reader.ReadToEnd()
|
||||
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 {
|
||||
Write-Host $_.Exception.Message -ForegroundColor Red
|
||||
|
||||
@ -66,15 +66,15 @@ internal sealed class SmallBagTrackOrderClient(
|
||||
var appKey = settings.AppKey.Trim();
|
||||
var timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||||
var sign = SmallBagTrackSignHelper.GenerateSign(appKey, timestamp, settings.AppSecret.Trim());
|
||||
var payload = SmallBagTrackUpsertApiRequest.FromBusinessRequest(request, appKey, timestamp, sign);
|
||||
var url = $"{settings.BaseUrl.Trim().TrimEnd('/')}/OrderV2/UpsertSmallBag";
|
||||
var requestJson = JsonSerializer.Serialize(payload, JsonOptions);
|
||||
var body = SmallBagTrackUpsertApiRequest.FromBusinessRequest(request);
|
||||
var url = SmallBagTrackUpsertApiRequest.BuildRequestUrl(settings.BaseUrl, appKey, timestamp, sign);
|
||||
var requestJson = JsonSerializer.Serialize(body, JsonOptions);
|
||||
|
||||
var client = httpClientFactory.CreateClient(HttpClientName);
|
||||
client.Timeout = TimeSpan.FromSeconds(Math.Max(5, settings.TimeoutSeconds));
|
||||
|
||||
using var response = await client.PostAsJsonAsync(url, payload, cancellationToken);
|
||||
var body = await response.Content.ReadAsStringAsync(cancellationToken);
|
||||
using var response = await client.PostAsJsonAsync(url, body, cancellationToken);
|
||||
var responseBody = await response.Content.ReadAsStringAsync(cancellationToken);
|
||||
return new SmallBagTrackUpsertResult
|
||||
{
|
||||
Skipped = false,
|
||||
@ -82,7 +82,7 @@ internal sealed class SmallBagTrackOrderClient(
|
||||
Url = url,
|
||||
RequestJson = requestJson,
|
||||
StatusCode = (int)response.StatusCode,
|
||||
ResponseBody = body
|
||||
ResponseBody = responseBody
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -4,13 +4,26 @@ using System.Text;
|
||||
namespace Oms.Application.Integrations.SmallBagTrack;
|
||||
|
||||
/// <summary>
|
||||
/// 轨迹服务 API 签名:appKey={}&timestamp={}&appSecret={} → MD5 大写。
|
||||
/// 轨迹服务 API 签名(与轨迹平台官方算法一致)。
|
||||
/// </summary>
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@ -1,16 +1,10 @@
|
||||
namespace Oms.Application.Integrations.SmallBagTrack;
|
||||
|
||||
/// <summary>
|
||||
/// POST /OrderV2/UpsertSmallBag 完整请求体(含公共参数 + 业务字段)。
|
||||
/// POST /OrderV2/UpsertSmallBag 业务请求体(公共参数 appKey/timestamp/sign 走 Query)。
|
||||
/// </summary>
|
||||
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 OrderNo { get; set; } = string.Empty;
|
||||
@ -27,16 +21,9 @@ internal sealed class SmallBagTrackUpsertApiRequest
|
||||
|
||||
public SmallBagTrackConsigneeRequest Consignee { get; set; } = new();
|
||||
|
||||
public static SmallBagTrackUpsertApiRequest FromBusinessRequest(
|
||||
SmallBagTrackUpsertRequest request,
|
||||
string appKey,
|
||||
long timestamp,
|
||||
string sign) =>
|
||||
public static SmallBagTrackUpsertApiRequest FromBusinessRequest(SmallBagTrackUpsertRequest request) =>
|
||||
new()
|
||||
{
|
||||
AppKey = appKey,
|
||||
Timestamp = timestamp,
|
||||
Sign = sign,
|
||||
Client = request.Client,
|
||||
OrderNo = request.OrderNo,
|
||||
TrackNo = request.TrackNo,
|
||||
@ -46,4 +33,15 @@ internal sealed class SmallBagTrackUpsertApiRequest
|
||||
ApiCode = request.ApiCode,
|
||||
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
|
||||
{
|
||||
[Fact]
|
||||
public void GenerateSign_ShouldMatchMd5UppercaseRule()
|
||||
private const string AppKey = "W2606001";
|
||||
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";
|
||||
const long timestamp = 1700000000;
|
||||
const string appSecret = "ziK8UzlQ39m3kdL346XEBg8nljY8os52";
|
||||
var signString = $"appKey={AppKey}×tamp={timestamp}&appSecret={AppSecret}";
|
||||
|
||||
var sign = SmallBagTrackSignHelper.GenerateSign(appKey, timestamp, appSecret);
|
||||
|
||||
sign.Should().Be("A744D764DEE7C925AD6750257A4DEEE7");
|
||||
SmallBagTrackSignHelper.GenerateSign(AppKey, timestamp, AppSecret)
|
||||
.Should()
|
||||
.Be(expected, because: $"signString={signString}");
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user