我目前正在尝试 Ping Payfast API 以获得基本身份验证率。
我认为我一直在努力让签名正确。
生成我的签名
按字母顺序对所有变量进行排序(如此处所示)和 URLencoding
string signature = HttpUtility.UrlEncode($"merchant-id={merchantID}&passphrase={passphrase}×tamp={timestamp}&version={version}");
然后我生成 MD5 哈希字符串,如下所示
using (var md5Hash = MD5.Create())
{
// Byte array representation of source string
var sourceBytes = Encoding.UTF8.GetBytes(signature);
// Generate hash value(Byte Array) for input data
var hashBytes = md5Hash.ComputeHash(sourceBytes);
// Convert hash byte array to string
var hash = BitConverter.ToString(hashBytes).Replace("-", string.Empty);
// Output the MD5 hash
Console.WriteLine(signature + " is: " + hash);
string hashlower = hash.ToLower()
}
然后我将签名添加到标题中
request.AddHeader("merchant-id", merchantID);
request.AddHeader("version", version);
request.AddHeader("signature", hashlower);
request.AddHeader("timestamp", timestamp);
在查看邮递员集合中的示例时,这似乎是正确的 https://documenter.getpostman.com/view/10608852/TVCmSQZu
有人能发现我在签名生成中做错的事情吗
完整代码
string timestamp = DateTime.UtcNow.ToString("yyyy-MM-ddTHH\\:mm\\:ss", CultureInfo.InvariantCulture);
string signature = HttpUtility.UrlEncode($"merchant-id={merchantID}&passphrase={passphrase}×tamp={timestamp}&version={version}");
using (var md5Hash = MD5.Create())
{
// Byte array representation of source string
var sourceBytes = Encoding.UTF8.GetBytes(signature);
// Generate hash value(Byte Array) for input data
var hashBytes = md5Hash.ComputeHash(sourceBytes);
// Convert hash byte array to string
var hash = BitConverter.ToString(hashBytes).Replace("-", string.Empty);
// Output the MD5 hash
Console.WriteLine(signature + " is: " + hash);
string hashlower = hash.ToLower();
var client = new RestClient("https://api.payfast.co.za/ping");
client.Timeout = -1;
var request = new RestRequest(Method.GET);
request.AddHeader("merchant-id", merchantID);
request.AddHeader("version", version);
request.AddHeader("signature", hashlower);
request.AddHeader("timestamp", timestamp);
IRestResponse response = client.Execute(request);
Console.WriteLine(response.Content);
}
我获取了 API 文档,并在复制后逐行转换匹配所有内容,并运行其 Node.js 和 Python 示例代码。
我的发现中有两件事。
URL 编码。
当您在c#中进行URL编码时,例如您的日期:
2023-02-20T20:00:00变为2023-02-20T20%3a00%3a00
PayFast 编码为大写,因此:
2023-02-20T20:00:00 变为 2023-02-20T20%3A00%3A00
注意后一个实例中的 'A',确保您的 URL 编码正确。
使用
Uri.EscapeDataString([string])
哈希编码:
您假设 C# 中使用 UTF-8 的编码与 Node.js 和 Python 中的加密哈希相同,在花时间研究所有变体后,它实际上是 ASCII。更新以下内容:
var sourceBytes = Encoding.UTF8.GetBytes(signature);
以下内容:
var sourceBytes = Encoding.ASCII.GetBytes(signature);
十六进制你的哈希值!
在返回哈希值之前,您还需要对其进行十六进制处理
var sb = new StringBuilder(hash.Length * 2);
foreach (byte b in hash)
{
sb.Append(b.ToString("x2")); // x2 is lowercase
}
signature = sb.ToString();
将所有内容包装到一个漂亮的全局方法中,并以
SortedList<string,string>
作为输入,迭代这些值,用 StringBuilder
将它们串起来,MD5 对其进行哈希处理,然后返回您的签名。
(是的,当您在 TKey 字符串上输入值时,
SortedList<TKey,TValue>
会自动对列表进行排序,这是 API 文档中的要求。)
这是一个有效的实现,其中“数据”包含密码。
private string GenerateSignature(Dictionary<string, string> data)
{
var orderedData = new SortedList<string, string>(data);
var getString =
string.Join("&", orderedData.Select(item => $"{item.Key}={Uri.EscapeDataString(item.Value)}"));
using var md5 = MD5.Create();
var hashedBytes = md5.ComputeHash(Encoding.ASCII.GetBytes(getString));
var sb = new StringBuilder(hashedBytes.Length * 2);
foreach (byte b in hashedBytes)
{
sb.Append(b.ToString("x2"));
}
return sb.ToString();
}