特来电参数
This commit is contained in:
parent
ae071b8be6
commit
0a65290940
30
docs/Teld测试环境2.0参数.md
Normal file
30
docs/Teld测试环境2.0参数.md
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
| 【特来电】互联互通对接信息登记表 | | | | |
|
||||
|------------------------|--------------------------------------------------|----------------------------------------------------------|---|---|
|
||||
| | | | | |
|
||||
| 类型 | 测试环境 | | | |
|
||||
| 组织机构代码(营业执照): | 395815801 | 平台对接的运营商ID(OperatorID) | | |
|
||||
| 企业名称: | 特来电 | | | |
|
||||
| 运营商秘钥(OperatorSecret): | 1234567890abcdef | 第三方调用特来电密钥 | | |
|
||||
| 签名秘钥(SigSecret): | 1234567890abcdef | | | |
|
||||
| 数据加密秘钥(DataSecret): | 1234567890abcdef | | | |
|
||||
| 初始化向量(DataSecretIV): | 1234567890abcdef | | | |
|
||||
| 测试环境互联互通url地址: | http://hlht.teld9.xyz/evcs/fv2016/Teld/商户组织机构代码/ | | | |
|
||||
| 其他: | | | | |
|
||||
| | | | | |
|
||||
| | | | | |
|
||||
| 【第三方】互联互通对接信息登记表 | | | | |
|
||||
| | | | | |
|
||||
| 类型 | 测试环境 | | | |
|
||||
| 组织机构代码(营业执照): | | 用于创建运营商ID,是9位组织机构代码(三证合一后18位的统一社会信用代码的第9~17位),与生产环境保持一致。 | | |
|
||||
| 企业名称: | | | | |
|
||||
| 运营商秘钥(OperatorSecret): | | 特来电调用第三方密钥 | | |
|
||||
| 签名秘钥(SigSecret): | | | | |
|
||||
| 数据加密秘钥(DataSecret): | | | | |
|
||||
| 初始化向量(DataSecretIV): | | | | |
|
||||
| 测试环境互联互通url地址: | | | | |
|
||||
| 其他: | | | | |
|
||||
| | | | | |
|
||||
| | | | | |
|
||||
| | | | | |
|
||||
| | | | | |
|
||||
| | | | | |
|
||||
|
|
@ -7,6 +7,11 @@ namespace HuangyanParking.Api.Controllers;
|
|||
/// <summary>
|
||||
/// 特来电回调控制器
|
||||
/// 接收特来电推送的充电订单和设备状态
|
||||
///
|
||||
/// 密钥说明(互联互通标准双向密钥):
|
||||
/// - DataSecret/SigSecret:我方调特来电时用的密钥(特来电提供)
|
||||
/// - PeerDataSecret/PeerSigSecret:特来电调我方时用的密钥(我方提供)
|
||||
/// 本控制器处理的是特来电调我方,所以用Peer系列密钥
|
||||
/// </summary>
|
||||
[ApiController]
|
||||
[Route("api/callback/tld")]
|
||||
|
|
@ -31,7 +36,6 @@ public class TldCallbackController : ControllerBase
|
|||
|
||||
/// <summary>
|
||||
/// 充电订单推送回调
|
||||
/// 特来电接口名:notification_charge_order_info
|
||||
/// POST /api/callback/tld/notification_charge_order_info
|
||||
/// </summary>
|
||||
[HttpPost("notification_charge_order_info")]
|
||||
|
|
@ -45,7 +49,6 @@ public class TldCallbackController : ControllerBase
|
|||
|
||||
/// <summary>
|
||||
/// 设备状态变化推送回调
|
||||
/// 特来电接口名:notification_stationStatus
|
||||
/// POST /api/callback/tld/notification_stationStatus
|
||||
/// </summary>
|
||||
[HttpPost("notification_stationStatus")]
|
||||
|
|
@ -57,22 +60,35 @@ public class TldCallbackController : ControllerBase
|
|||
|
||||
/// <summary>
|
||||
/// 平台认证接口(特来电调用我方获取Token)
|
||||
/// 特来电接口名:query_token
|
||||
/// POST /api/callback/tld/query_token
|
||||
/// </summary>
|
||||
[HttpPost("query_token")]
|
||||
public ActionResult<TldResponse> QueryToken([FromBody] TldRequest request)
|
||||
{
|
||||
var tldConfig = _configuration.GetSection("Tld");
|
||||
var dataSecret = tldConfig["DataSecret"]!;
|
||||
var dataSecretIV = tldConfig["DataSecretIV"]!;
|
||||
var sigSecret = tldConfig["SigSecret"]!;
|
||||
// 特来电调我方,用我方密钥解密
|
||||
var peerDataSecret = tldConfig["PeerDataSecret"]!;
|
||||
var peerDataSecretIV = tldConfig["PeerDataSecretIV"]!;
|
||||
var peerSigSecret = tldConfig["PeerSigSecret"]!;
|
||||
|
||||
// 验签
|
||||
if (!string.IsNullOrEmpty(request.Sig))
|
||||
{
|
||||
var sigValid = _tldCrypto.Verify(
|
||||
request.OperatorID, request.Data, request.TimeStamp, request.Seq,
|
||||
request.Sig, peerSigSecret);
|
||||
if (!sigValid)
|
||||
{
|
||||
_logger.LogWarning("特来电Token请求验签失败");
|
||||
return Ok(new TldResponse { Ret = 4001, Msg = "签名错误" });
|
||||
}
|
||||
}
|
||||
|
||||
// 解密请求
|
||||
string decryptedData;
|
||||
try
|
||||
{
|
||||
decryptedData = _tldCrypto.Decrypt(request.Data, dataSecret, dataSecretIV);
|
||||
decryptedData = _tldCrypto.Decrypt(request.Data, peerDataSecret, peerDataSecretIV);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
@ -85,7 +101,7 @@ public class TldCallbackController : ControllerBase
|
|||
var tokenRequest = JsonSerializer.Deserialize<TldTokenRequest>(decryptedData);
|
||||
var expectedSecret = tldConfig["PeerOperatorSecret"];
|
||||
|
||||
// 验证对方的OperatorSecret(如果配置了的话)
|
||||
// 验证对方的OperatorSecret
|
||||
if (!string.IsNullOrEmpty(expectedSecret) &&
|
||||
tokenRequest?.OperatorSecret != expectedSecret)
|
||||
{
|
||||
|
|
@ -93,12 +109,12 @@ public class TldCallbackController : ControllerBase
|
|||
{
|
||||
OperatorID = request.OperatorID,
|
||||
SuccStat = 1,
|
||||
FailReason = 2 // 密钥错误
|
||||
FailReason = 2
|
||||
};
|
||||
return Ok(BuildEncryptedResponse(failResult));
|
||||
}
|
||||
|
||||
// 生成Token(使用简单的GUID,实际可用JWT)
|
||||
// 生成Token
|
||||
var token = request.OperatorID + DateTime.Now.ToString("yyyyMMddHHmmss") +
|
||||
Guid.NewGuid().ToString("N")[..32];
|
||||
var result = new TldTokenResult
|
||||
|
|
@ -113,19 +129,22 @@ public class TldCallbackController : ControllerBase
|
|||
return Ok(BuildEncryptedResponse(result));
|
||||
}
|
||||
|
||||
/// <summary>构建加密响应(加密Data + 响应签名)</summary>
|
||||
/// <summary>
|
||||
/// 构建加密响应
|
||||
/// 响应给特来电时,用我方密钥加密和签名
|
||||
/// </summary>
|
||||
private TldResponse BuildEncryptedResponse<T>(T data)
|
||||
{
|
||||
var tldConfig = _configuration.GetSection("Tld");
|
||||
var dataSecret = tldConfig["DataSecret"]!;
|
||||
var dataSecretIV = tldConfig["DataSecretIV"]!;
|
||||
var sigSecret = tldConfig["SigSecret"]!;
|
||||
var peerDataSecret = tldConfig["PeerDataSecret"]!;
|
||||
var peerDataSecretIV = tldConfig["PeerDataSecretIV"]!;
|
||||
var peerSigSecret = tldConfig["PeerSigSecret"]!;
|
||||
|
||||
var responseJson = JsonSerializer.Serialize(data);
|
||||
var encryptedData = _tldCrypto.Encrypt(responseJson, dataSecret, dataSecretIV);
|
||||
var encryptedData = _tldCrypto.Encrypt(responseJson, peerDataSecret, peerDataSecretIV);
|
||||
|
||||
// 响应签名拼接顺序:Ret + Msg + Data
|
||||
var sig = _tldCrypto.SignResponse(0, "", encryptedData, sigSecret);
|
||||
var sig = _tldCrypto.SignResponse(0, "", encryptedData, peerSigSecret);
|
||||
|
||||
return new TldResponse
|
||||
{
|
||||
|
|
|
|||
|
|
@ -14,12 +14,17 @@
|
|||
"AppSecret": "c227aad12a3fbd0d5413759424124150"
|
||||
},
|
||||
"Tld": {
|
||||
"BaseUrl": "https://your-tld-api-url",
|
||||
"OperatorID": "your_operator_id",
|
||||
"OperatorSecret": "your_operator_secret",
|
||||
"DataSecret": "your_data_secret",
|
||||
"DataSecretIV": "your_data_secret_iv",
|
||||
"SigSecret": "your_sig_secret"
|
||||
"BaseUrl": "http://hlht.teld9.xyz/evcs/fv2016/Teld/MA2DT09B8",
|
||||
"OperatorID": "MA2DT09B8",
|
||||
"OperatorSecret": "1234567890abcdef",
|
||||
"DataSecret": "1234567890abcdef",
|
||||
"DataSecretIV": "1234567890abcdef",
|
||||
"SigSecret": "1234567890abcdef",
|
||||
"TldOperatorID": "395815801",
|
||||
"PeerOperatorSecret": "WUscAwtCHz5Uogqg",
|
||||
"PeerDataSecret": "5uBBwJjf71uboUq8",
|
||||
"PeerDataSecretIV": "5qCP6f5Jbx7J8Ird",
|
||||
"PeerSigSecret": "UEVo9ruFt8aPSt73"
|
||||
},
|
||||
"Ygl": {
|
||||
"BaseUrl": "https://api-test.1kmxc.com",
|
||||
|
|
|
|||
|
|
@ -29,6 +29,10 @@ public class TldService : ITldService
|
|||
private readonly string _dataSecret;
|
||||
private readonly string _dataSecretIV;
|
||||
private readonly string _sigSecret;
|
||||
// 我方密钥(特来电调用我方时使用)
|
||||
private readonly string _peerDataSecret;
|
||||
private readonly string _peerDataSecretIV;
|
||||
private readonly string _peerSigSecret;
|
||||
|
||||
private const string TokenCacheKey = "tld:access_token";
|
||||
|
||||
|
|
@ -55,6 +59,9 @@ public class TldService : ITldService
|
|||
_dataSecret = tldConfig["DataSecret"]!;
|
||||
_dataSecretIV = tldConfig["DataSecretIV"]!;
|
||||
_sigSecret = tldConfig["SigSecret"]!;
|
||||
_peerDataSecret = tldConfig["PeerDataSecret"]!;
|
||||
_peerDataSecretIV = tldConfig["PeerDataSecretIV"]!;
|
||||
_peerSigSecret = tldConfig["PeerSigSecret"]!;
|
||||
}
|
||||
|
||||
/// <summary>平台认证,获取Token(带Redis缓存)</summary>
|
||||
|
|
@ -122,12 +129,12 @@ public class TldService : ITldService
|
|||
/// </summary>
|
||||
public async Task<TldChargeOrderResponse> HandleChargeOrderAsync(TldRequest request)
|
||||
{
|
||||
// 1. 验签
|
||||
// 1. 验签(特来电用我方密钥签名,所以用PeerSigSecret验签)
|
||||
if (!string.IsNullOrEmpty(request.Sig))
|
||||
{
|
||||
var isValid = _crypto.Verify(
|
||||
request.OperatorID, request.Data, request.TimeStamp, request.Seq,
|
||||
request.Sig, _sigSecret);
|
||||
request.Sig, _peerSigSecret);
|
||||
|
||||
if (!isValid)
|
||||
{
|
||||
|
|
@ -136,8 +143,8 @@ public class TldService : ITldService
|
|||
}
|
||||
}
|
||||
|
||||
// 2. 解密订单数据
|
||||
var decryptedData = _crypto.Decrypt(request.Data, _dataSecret, _dataSecretIV);
|
||||
// 2. 解密订单数据(特来电用我方密钥加密,所以用PeerDataSecret解密)
|
||||
var decryptedData = _crypto.Decrypt(request.Data, _peerDataSecret, _peerDataSecretIV);
|
||||
_logger.LogInformation("特来电充电订单推送: {Data}", decryptedData);
|
||||
|
||||
var order = JsonSerializer.Deserialize<TldChargeOrder>(decryptedData);
|
||||
|
|
@ -228,13 +235,32 @@ public class TldService : ITldService
|
|||
};
|
||||
}
|
||||
|
||||
// 6. 发放积分
|
||||
await _pointsService.GrantPointsAsync(
|
||||
userId.Value, points,
|
||||
$"充电积分:消费{order.TotalMoney:F2}元",
|
||||
tldOrderId: order.StartChargeSeq);
|
||||
// 6. 发放积分(使用事务确保一致性)
|
||||
await using var transaction = await _db.Database.BeginTransactionAsync();
|
||||
try
|
||||
{
|
||||
await _pointsService.GrantPointsAsync(
|
||||
userId.Value, points,
|
||||
$"充电积分:消费{order.TotalMoney:F2}元",
|
||||
tldOrderId: order.StartChargeSeq);
|
||||
|
||||
await _db.SaveChangesAsync();
|
||||
// 保存充电记录的更新(如果有)
|
||||
if (chargeRecord is not null)
|
||||
await _db.SaveChangesAsync();
|
||||
|
||||
await transaction.CommitAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await transaction.RollbackAsync();
|
||||
_logger.LogError(ex, "特来电订单积分发放失败: OrderId={OrderId}", order.StartChargeSeq);
|
||||
return new TldChargeOrderResponse
|
||||
{
|
||||
StartChargeSeq = order.StartChargeSeq,
|
||||
ConnectorID = order.ConnectorID,
|
||||
ConfirmResult = 1
|
||||
};
|
||||
}
|
||||
|
||||
_logger.LogInformation("特来电订单积分发放完成: OrderId={OrderId}, UserId={UserId}, Points={Points}",
|
||||
order.StartChargeSeq, userId.Value, points);
|
||||
|
|
@ -254,12 +280,12 @@ public class TldService : ITldService
|
|||
/// </summary>
|
||||
public async Task<TldStationStatusCallbackResponse> HandleStationStatusAsync(TldRequest request)
|
||||
{
|
||||
// 验签
|
||||
// 验签(特来电用我方密钥签名)
|
||||
if (!string.IsNullOrEmpty(request.Sig))
|
||||
{
|
||||
var isValid = _crypto.Verify(
|
||||
request.OperatorID, request.Data, request.TimeStamp, request.Seq,
|
||||
request.Sig, _sigSecret);
|
||||
request.Sig, _peerSigSecret);
|
||||
|
||||
if (!isValid)
|
||||
{
|
||||
|
|
@ -268,8 +294,8 @@ public class TldService : ITldService
|
|||
}
|
||||
}
|
||||
|
||||
// 解密并缓存到Redis(供前端查询设备状态)
|
||||
var decryptedData = _crypto.Decrypt(request.Data, _dataSecret, _dataSecretIV);
|
||||
// 解密(特来电用我方密钥加密)并缓存到Redis
|
||||
var decryptedData = _crypto.Decrypt(request.Data, _peerDataSecret, _peerDataSecretIV);
|
||||
_logger.LogDebug("特来电设备状态变化: {Data}", decryptedData);
|
||||
|
||||
try
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user