1

我真的会把头发拉出来。这应该不难实现,但无论如何我无法让它工作。我正在尝试实施瑞典 Swish API 进行支付。我们所需要的只是发送一个有效载荷并向 HttpClientHandler 添加一个证书。

我正在尝试按照此处所述实施 PaymentRequest 方法:https ://developer.swish.nu/api/payment-request/v2

我的代码如下,首先是一个配置类:

public class SwishApiDetails
{
    public string BaseUrl { get; set; } // Base URL for the API
    public string CallbackUrl { get; set; } // Optional callback URL
    public string ClientCertificate { get; set; } // Client certificate thumbprint
    public string RootCertificateV1 { get; set; } // First CA certificate
    public string RootCertificateV2 { get; set; } // Second CA certificate
}

实际 API 调用的代码(是的,我知道我没有处理客户端,但这不是现在的问题(除非是这样))

public class SwishApi
{
    private readonly HttpClient _client;
    private readonly SwishApiDetails _configuration;

    public SwishApi(SwishApiDetails configuration)
    {
        _configuration = configuration;
        var handler = new HttpClientHandler
        {
            ServerCertificateCustomValidationCallback = (
                sender,
                certificate,
                chain,
                sslPolicyErrors) => true,
            ClientCertificateOptions = ClientCertificateOption.Manual
        };

        var thumbprints = new List<string>
        {
            _configuration.ClientCertificate,
            _configuration.RootCertificateV1,
            _configuration.RootCertificateV2
        };

        var store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
        store.Open(OpenFlags.ReadOnly);

        foreach (var thumbprint in thumbprints)
        {
            var certificates = store.Certificates.Find(X509FindType.FindByThumbprint, thumbprint, false);
            handler.ClientCertificates.AddRange(certificates);
        }

        _client = new HttpClient(handler) {BaseAddress = new Uri(_configuration.BaseUrl)};
    }

    public async Task<(bool HasError, string Message)> MakePaymentRequest(
        int amountToPay,
        string reference,
        string phoneNumber,
        string receiverAlias,
        string messageToCustomer)
    {
        var phoneAlias = phoneNumber.Replace(" ", "").Replace("+", "");
        if (phoneAlias.StartsWith("0")) phoneAlias = phoneAlias.Substring(1, phoneAlias.Length);

        var result = await _client.SendAsync(new HttpRequestMessage
        {
            Method = HttpMethod.Put,
            RequestUri = new Uri($"{_configuration.BaseUrl}/api/v2/paymentrequests/{reference}"),
            Content = JsonContent.Create(new
            {
                amount = amountToPay,
                payerAlias = phoneAlias,
                payeeAlias = receiverAlias,
                message = messageToCustomer,
                payeePaymentReference = reference,
                currency = SwishDefaults.Currency.Sek,
                callbackUrl = _configuration.CallbackUrl
            })
        });

        return result.IsSuccessStatusCode
            ? (false, string.Empty)
            : (true, await result.Content.ReadAsStringAsync());
    }
}

为了测试这一切,我正在使用这段代码:

public class Test
{
    private readonly SwishApi _swish;

    public Test()
    {
        _swish = new SwishApi(new SwishApiDetails
        {
            BaseUrl = "https://cpc.getswish.net/swish-cpcapi",
            CallbackUrl = null,
            ClientCertificate = "F06644FAF53150D5B31716ABF121FE112A225AF1", // Local thumbprint
            RootCertificateV1 = "A8985D3A65E5E5C4B2D7D66D40C6DD2FB19C5436", // Local thumbprint
            RootCertificateV2 = "03BFF7B54C712504C5BE5A8528163C931618A3C0" // Local thumbprint
        });
    }

    [Fact]
    public async Task TestPaymentRequest()
    {
        var (hasError, _) = await _swish.MakePaymentRequest(
            1,
            Guid.NewGuid().ToString("N").ToUpper(),
            "4671111111",
            "Swish Test",
            "Testing");

        Assert.False(hasError);
    }
}

证书从这里下载:https ://assets.ctfassets.net/4dca8u8ebqnn/5B6HqHPnsnGf0klDoeQSJh/1d4a6bf66a1269859cf00c01b312c601/mss_test_1.9.zip

它们对于测试环境是公开的。

证书不是由真正的 CA 签发的,它们是由 Swish 自己签名的,因此可能存在一些问题。除此之外,我真的不知道出了什么问题以及为什么其他人可以让它工作。

它可能是特定于操作系统的,还是我机器上的密码套件被破坏了?

如果你想试试这个,去这个 repo:https ://github.com/lhammarstrom/swish-net

如果您想使用本地证书文件进行尝试,则可以转到此分支:https ://github.com/lhammarstrom/swish-net/tree/feature/with_cert (feature/with_cert)。客户端证书包含在测试项目的文件中。

另外,为了澄清,我得到的错误是:

System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception.

System.Net.Http.HttpRequestException
The SSL connection could not be established, see inner exception.
   at System.Net.Http.ConnectHelper.EstablishSslConnectionAsyncCore(Boolean async, Stream stream, SslClientAuthenticationOptions sslOptions, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.ConnectAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.CreateHttp11ConnectionAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.GetHttpConnectionAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.SendWithRetryAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken)
   at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.HttpClient.SendAsyncCore(HttpRequestMessage request, HttpCompletionOption completionOption, Boolean async, Boolean emitTelemetryStartStop, CancellationToken cancellationToken)
   at Swish.Services.SwishApi.MakePaymentRequest(Int32 amountToPay, String reference, String phoneNumber, String receiverAlias, String messageToCustomer) in /Users/leni/Projects/Swish/Swish/Services/SwishApi.cs:line 73
   at Swish.Tests.Test.TestPaymentRequest() in /Users/leni/Projects/Swish/Swish.Tests/Tests.cs:line 30
   at Xunit.Sdk.TestInvoker`1.<>c__DisplayClass48_1.<<InvokeTestMethodAsync>b__1>d.MoveNext() in C:\Dev\xunit\xunit\src\xunit.execution\Sdk\Frameworks\Runners\TestInvoker.cs:line 264
--- End of stack trace from previous location ---
   at Xunit.Sdk.ExecutionTimer.AggregateAsync(Func`1 asyncAction) in C:\Dev\xunit\xunit\src\xunit.execution\Sdk\Frameworks\ExecutionTimer.cs:line 48
   at Xunit.Sdk.ExceptionAggregator.RunAsync(Func`1 code) in C:\Dev\xunit\xunit\src\xunit.core\Sdk\ExceptionAggregator.cs:line 90

System.Security.Authentication.AuthenticationException
Authentication failed, see inner exception.
   at System.Net.Security.SslStream.ForceAuthenticationAsync[TIOAdapter](TIOAdapter adapter, Boolean receiveFirst, Byte[] reAuthenticationData, Boolean isApm)
   at System.Net.Http.ConnectHelper.EstablishSslConnectionAsyncCore(Boolean async, Stream stream, SslClientAuthenticationOptions sslOptions, CancellationToken cancellationToken)

Interop+AppleCrypto+SslException
handshake failure
  Exception doesn't have a stacktrace

编辑:

我已遵循本指南(https://johan.driessen.se/posts/Calling-the-Swish-Payment-API-from-Azure-AppService/)。他们让它工作,我没有。此外,这个声称可以在 .NET 5 ( https://github.com/RickardPettersson/swish-api-csharp ) 上运行的存储库也无法在我的机器上运行。

4

0 回答 0