diff options
Diffstat (limited to 'src/IOL.VippsEcommerce/VippsEcommerceService.cs')
| -rw-r--r-- | src/IOL.VippsEcommerce/VippsEcommerceService.cs | 933 |
1 files changed, 466 insertions, 467 deletions
diff --git a/src/IOL.VippsEcommerce/VippsEcommerceService.cs b/src/IOL.VippsEcommerce/VippsEcommerceService.cs index 84bbf79..64802ca 100644 --- a/src/IOL.VippsEcommerce/VippsEcommerceService.cs +++ b/src/IOL.VippsEcommerce/VippsEcommerceService.cs @@ -11,562 +11,561 @@ using IOL.VippsEcommerce.Models.Api; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -namespace IOL.VippsEcommerce +namespace IOL.VippsEcommerce; + +/// <summary> +/// The main class for interacting with the vipps api. +/// </summary> +public class VippsEcommerceService : IVippsEcommerceService { - /// <summary> - /// The main class for interacting with the vipps api. - /// </summary> - public class VippsEcommerceService : IVippsEcommerceService - { - private readonly HttpClient _client; - private readonly ILogger<VippsEcommerceService> _logger; - private readonly string _vippsClientId; - private readonly string _vippsClientSecret; - private readonly string _vippsMsn; - private readonly string _cacheEncryptionKey; - private readonly string _cacheDirectoryPath; + private readonly HttpClient _client; + private readonly ILogger<VippsEcommerceService> _logger; + private readonly string _vippsClientId; + private readonly string _vippsClientSecret; + private readonly string _vippsMsn; + private readonly string _cacheEncryptionKey; + private readonly string _cacheDirectoryPath; - private readonly JsonSerializerOptions _requestJsonSerializerOptions = new() { - IgnoreNullValues = true - }; + private readonly JsonSerializerOptions _requestJsonSerializerOptions = new() { + IgnoreNullValues = true + }; - private const string VIPPS_CACHE_FILE_NAME = "vipps_ecommerce_credentials.json"; - private string CacheFilePath => Path.Combine(_cacheDirectoryPath, VIPPS_CACHE_FILE_NAME); + private const string VIPPS_CACHE_FILE_NAME = "vipps_ecommerce_credentials.json"; + private string CacheFilePath => Path.Combine(_cacheDirectoryPath, VIPPS_CACHE_FILE_NAME); - public VippsConfiguration Configuration { get; } + public VippsConfiguration Configuration { get; } - public VippsEcommerceService( - HttpClient client, - ILogger<VippsEcommerceService> logger, - IOptions<VippsConfiguration> options - ) { - Configuration = options.Value; - Configuration.Verify(); - var vippsApiUrl = Configuration.ApiUrl; - client.BaseAddress = new Uri(vippsApiUrl); - _client = client; - _logger = logger; - _vippsClientId = Configuration.ClientId; - _vippsClientSecret = Configuration.ClientSecret; - client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", - Configuration.PrimarySubscriptionKey - ?? Configuration.SecondarySubscriptionKey); + public VippsEcommerceService( + HttpClient client, + ILogger<VippsEcommerceService> logger, + IOptions<VippsConfiguration> options + ) { + Configuration = options.Value; + Configuration.Verify(); + var vippsApiUrl = Configuration.ApiUrl; + client.BaseAddress = new Uri(vippsApiUrl); + _client = client; + _logger = logger; + _vippsClientId = Configuration.ClientId; + _vippsClientSecret = Configuration.ClientSecret; + client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", + Configuration.PrimarySubscriptionKey + ?? Configuration.SecondarySubscriptionKey); - var msn = Configuration.MerchantSerialNumber; - if (msn.IsPresent()) { - client.DefaultRequestHeaders.Add("Merchant-Serial-Number", msn); - _vippsMsn = msn; - } + var msn = Configuration.MerchantSerialNumber; + if (msn.IsPresent()) { + client.DefaultRequestHeaders.Add("Merchant-Serial-Number", msn); + _vippsMsn = msn; + } - var systemName = Configuration.SystemName; - if (systemName.IsPresent()) { - client.DefaultRequestHeaders.Add("Vipps-System-Name", systemName); - } + var systemName = Configuration.SystemName; + if (systemName.IsPresent()) { + client.DefaultRequestHeaders.Add("Vipps-System-Name", systemName); + } - var systemVersion = Configuration.SystemVersion; - if (systemVersion.IsPresent()) { - client.DefaultRequestHeaders.Add("Vipps-System-Version", systemVersion); - } + var systemVersion = Configuration.SystemVersion; + if (systemVersion.IsPresent()) { + client.DefaultRequestHeaders.Add("Vipps-System-Version", systemVersion); + } - var systemPluginName = Configuration.SystemPluginName; - if (systemPluginName.IsPresent()) { - client.DefaultRequestHeaders.Add("Vipps-System-Plugin-Name", systemPluginName); - } + var systemPluginName = Configuration.SystemPluginName; + if (systemPluginName.IsPresent()) { + client.DefaultRequestHeaders.Add("Vipps-System-Plugin-Name", systemPluginName); + } - var systemPluginVersion = Configuration.SystemPluginVersion; - if (systemPluginVersion.IsPresent()) { - client.DefaultRequestHeaders.Add("Vipps-System-Plugin-Version", systemPluginVersion); - } + var systemPluginVersion = Configuration.SystemPluginVersion; + if (systemPluginVersion.IsPresent()) { + client.DefaultRequestHeaders.Add("Vipps-System-Plugin-Version", systemPluginVersion); + } - _cacheEncryptionKey = Configuration.CacheEncryptionKey; - _cacheDirectoryPath = Configuration.CacheDirectoryPath; - if (_cacheDirectoryPath.IsPresent()) { - if (!_cacheDirectoryPath.IsDirectoryWritable()) { - _logger.LogError("Could not write to cache file directory (" - + _cacheDirectoryPath - + "). Disabling caching."); - _cacheDirectoryPath = default; - _cacheEncryptionKey = default; - } + _cacheEncryptionKey = Configuration.CacheEncryptionKey; + _cacheDirectoryPath = Configuration.CacheDirectoryPath; + if (_cacheDirectoryPath.IsPresent()) { + if (!_cacheDirectoryPath.IsDirectoryWritable()) { + _logger.LogError("Could not write to cache file directory (" + + _cacheDirectoryPath + + "). Disabling caching."); + _cacheDirectoryPath = default; + _cacheEncryptionKey = default; } - - _logger.LogInformation(nameof(VippsEcommerceService) - + " was successfully initialised with api url: " - + vippsApiUrl); } - /// <summary> - /// The access token endpoint is used to get the JWT (JSON Web Token) that must be passed in every API request in the Authorization header. - /// The access token is a base64-encoded string value that must be aquired first before making any Vipps api calls. - /// The access token is valid for 1 hour in the test environment and 24 hours in the production environment. - /// </summary> - /// <returns></returns> - /// <exception cref="HttpRequestException">Throws if the api returns unsuccessfully</exception> - private async Task<VippsAuthorizationTokenResponse> GetAuthorizationTokenAsync( - bool forceRefresh = false, - CancellationToken ct = default - ) { - if (!forceRefresh && _cacheDirectoryPath.IsPresent() && File.Exists(CacheFilePath)) { - var fileContents = await File.ReadAllTextAsync(CacheFilePath, ct); + _logger.LogInformation(nameof(VippsEcommerceService) + + " was successfully initialised with api url: " + + vippsApiUrl); + } - if (fileContents.IsPresent()) { - VippsAuthorizationTokenResponse credentials = default; - try { - credentials = JsonSerializer.Deserialize<VippsAuthorizationTokenResponse>(fileContents); - } catch (Exception e) { - if (e is JsonException && _cacheEncryptionKey.IsPresent()) { // most likely encrypted - try { - var decryptedContents = fileContents.DecryptWithAes(_cacheEncryptionKey); - credentials = - JsonSerializer.Deserialize<VippsAuthorizationTokenResponse>(decryptedContents); - } catch { - // ignored - } + /// <summary> + /// The access token endpoint is used to get the JWT (JSON Web Token) that must be passed in every API request in the Authorization header. + /// The access token is a base64-encoded string value that must be aquired first before making any Vipps api calls. + /// The access token is valid for 1 hour in the test environment and 24 hours in the production environment. + /// </summary> + /// <returns></returns> + /// <exception cref="HttpRequestException">Throws if the api returns unsuccessfully</exception> + private async Task<VippsAuthorizationTokenResponse> GetAuthorizationTokenAsync( + bool forceRefresh = false, + CancellationToken ct = default + ) { + if (!forceRefresh && _cacheDirectoryPath.IsPresent() && File.Exists(CacheFilePath)) { + var fileContents = await File.ReadAllTextAsync(CacheFilePath, ct); + + if (fileContents.IsPresent()) { + VippsAuthorizationTokenResponse credentials = default; + try { + credentials = JsonSerializer.Deserialize<VippsAuthorizationTokenResponse>(fileContents); + } catch (Exception e) { + if (e is JsonException && _cacheEncryptionKey.IsPresent()) { // most likely encrypted + try { + var decryptedContents = fileContents.DecryptWithAes(_cacheEncryptionKey); + credentials = + JsonSerializer.Deserialize<VippsAuthorizationTokenResponse>(decryptedContents); + } catch { + // ignored } } + } - if (credentials != default) { - var currentEpoch = new DateTimeOffset(DateTime.UtcNow).ToUnixTimeSeconds(); - if (long.TryParse(credentials.ExpiresOn, out var expires) - && credentials.AccessToken.IsPresent()) { - if (expires - 600 > currentEpoch) { - _logger.LogDebug(nameof(VippsEcommerceService) + ": Got tokens from cache"); - return credentials; - } + if (credentials != default) { + var currentEpoch = new DateTimeOffset(DateTime.UtcNow).ToUnixTimeSeconds(); + if (long.TryParse(credentials.ExpiresOn, out var expires) + && credentials.AccessToken.IsPresent()) { + if (expires - 600 > currentEpoch) { + _logger.LogDebug(nameof(VippsEcommerceService) + ": Got tokens from cache"); + return credentials; } } } } + } - var requestMessage = new HttpRequestMessage { - Headers = { - { - "client_id", _vippsClientId - }, { - "client_secret", _vippsClientSecret - }, - }, - RequestUri = new Uri(_client.BaseAddress + "accesstoken/get"), - Method = HttpMethod.Post - }; - var response = await _client.SendAsync(requestMessage, ct); - - try { - response.EnsureSuccessStatusCode(); - var credentials = await response.Content.ReadAsStringAsync(ct); - - if (_cacheDirectoryPath.IsPresent()) { - await File.WriteAllTextAsync(CacheFilePath, - _cacheEncryptionKey.IsPresent() - ? credentials.EncryptWithAes(_cacheEncryptionKey) - : credentials, - ct); - } + var requestMessage = new HttpRequestMessage { + Headers = { + { + "client_id", _vippsClientId + }, { + "client_secret", _vippsClientSecret + }, + }, + RequestUri = new Uri(_client.BaseAddress + "accesstoken/get"), + Method = HttpMethod.Post + }; + var response = await _client.SendAsync(requestMessage, ct); - _logger.LogDebug(nameof(VippsEcommerceService) + ": Got tokens from " + requestMessage.RequestUri); - return JsonSerializer.Deserialize<VippsAuthorizationTokenResponse>(credentials); - } catch (Exception e) { - var exception = new VippsRequestException(nameof(GetAuthorizationTokenAsync) + " failed.", e); - if (e is not HttpRequestException) { - throw exception; - } + try { + response.EnsureSuccessStatusCode(); + var credentials = await response.Content.ReadAsStringAsync(ct); - try { - exception.ErrorResponse = - await response.Content.ReadFromJsonAsync<VippsErrorResponse>(cancellationToken: ct); - _logger.LogError(nameof(GetAuthorizationTokenAsync) - + " Api error response: " - + JsonSerializer.Serialize(response.Content)); - } catch { - // ignored - } + if (_cacheDirectoryPath.IsPresent()) { + await File.WriteAllTextAsync(CacheFilePath, + _cacheEncryptionKey.IsPresent() + ? credentials.EncryptWithAes(_cacheEncryptionKey) + : credentials, + ct); + } + _logger.LogDebug(nameof(VippsEcommerceService) + ": Got tokens from " + requestMessage.RequestUri); + return JsonSerializer.Deserialize<VippsAuthorizationTokenResponse>(credentials); + } catch (Exception e) { + var exception = new VippsRequestException(nameof(GetAuthorizationTokenAsync) + " failed.", e); + if (e is not HttpRequestException) { throw exception; } - } - - /// <summary> - /// This API call allows the merchants to initiate payments. - /// The merchantSerialNumber (MSN) specifies which sales unit the payments is for. - /// Payments are uniquely identified with the merchantSerialNumber and orderId together. - /// The merchant-provided orderId must be unique per sales channel. - /// Once the transaction is successfully initiated in Vipps, you will receive a response with a fallBack URL which will direct the customer to the Vipps landing page. - /// The landing page detects if the request comes from a mobile or laptop/desktop device, and if on a mobile device automatically switches to the Vipps app if it is intalled. - /// The merchant may also pass the 'isApp: true' parameter that will make Vipps respond with a app-switch deeplink that will take the customer directly to the Vipps app. - /// URLs passed to Vipps must validate with the Apache Commons UrlValidator. - /// </summary> - /// <returns></returns> - /// <exception cref="HttpRequestException">Throws if the api returns unsuccessfully</exception> - public async Task<VippsInitiatePaymentResponse> InitiatePaymentAsync( - VippsInitiatePaymentRequest payload, - CancellationToken ct = default - ) { - if (_client.DefaultRequestHeaders.Authorization?.Parameter.IsNullOrWhiteSpace() ?? true) { - var credentials = await GetAuthorizationTokenAsync(false, ct); - _client.DefaultRequestHeaders.Authorization = - new AuthenticationHeaderValue("Bearer", credentials.AccessToken); + try { + exception.ErrorResponse = + await response.Content.ReadFromJsonAsync<VippsErrorResponse>(cancellationToken: ct); + _logger.LogError(nameof(GetAuthorizationTokenAsync) + + " Api error response: " + + JsonSerializer.Serialize(response.Content)); + } catch { + // ignored } - var response = await _client.PostAsJsonAsync("ecomm/v2/payments", - payload, - _requestJsonSerializerOptions, - ct); + throw exception; + } + } - try { - response.EnsureSuccessStatusCode(); - _logger.LogDebug(nameof(VippsEcommerceService) - + ": Successfully issued a request to " - + response.RequestMessage?.RequestUri); - return await response.Content - .ReadFromJsonAsync<VippsInitiatePaymentResponse>(cancellationToken: ct); - } catch (Exception e) { - var exception = new VippsRequestException(nameof(InitiatePaymentAsync) + " failed.", e); - if (e is not HttpRequestException) { - throw exception; - } - try { - exception.ErrorResponse = - await response.Content.ReadFromJsonAsync<VippsErrorResponse>(cancellationToken: ct); - _logger.LogError(nameof(InitiatePaymentAsync) - + " Api error response: " - + JsonSerializer.Serialize(response.Content)); - } catch { - // ignored - } + /// <summary> + /// This API call allows the merchants to initiate payments. + /// The merchantSerialNumber (MSN) specifies which sales unit the payments is for. + /// Payments are uniquely identified with the merchantSerialNumber and orderId together. + /// The merchant-provided orderId must be unique per sales channel. + /// Once the transaction is successfully initiated in Vipps, you will receive a response with a fallBack URL which will direct the customer to the Vipps landing page. + /// The landing page detects if the request comes from a mobile or laptop/desktop device, and if on a mobile device automatically switches to the Vipps app if it is intalled. + /// The merchant may also pass the 'isApp: true' parameter that will make Vipps respond with a app-switch deeplink that will take the customer directly to the Vipps app. + /// URLs passed to Vipps must validate with the Apache Commons UrlValidator. + /// </summary> + /// <returns></returns> + /// <exception cref="HttpRequestException">Throws if the api returns unsuccessfully</exception> + public async Task<VippsInitiatePaymentResponse> InitiatePaymentAsync( + VippsInitiatePaymentRequest payload, + CancellationToken ct = default + ) { + if (_client.DefaultRequestHeaders.Authorization?.Parameter.IsNullOrWhiteSpace() ?? true) { + var credentials = await GetAuthorizationTokenAsync(false, ct); + _client.DefaultRequestHeaders.Authorization = + new AuthenticationHeaderValue("Bearer", credentials.AccessToken); + } + var response = await _client.PostAsJsonAsync("ecomm/v2/payments", + payload, + _requestJsonSerializerOptions, + ct); + + try { + response.EnsureSuccessStatusCode(); + _logger.LogDebug(nameof(VippsEcommerceService) + + ": Successfully issued a request to " + + response.RequestMessage?.RequestUri); + return await response.Content + .ReadFromJsonAsync<VippsInitiatePaymentResponse>(cancellationToken: ct); + } catch (Exception e) { + var exception = new VippsRequestException(nameof(InitiatePaymentAsync) + " failed.", e); + if (e is not HttpRequestException) { throw exception; } - } - /// <summary> - /// This API call allows merchant to capture the reserved amount. - /// Amount to capture cannot be higher than reserved. - /// The API also allows capturing partial amount of the reserved amount. - /// Partial capture can be called as many times as required so long there is reserved amount to capture. - /// Transaction text is not optional and is used as a proof of delivery (tracking code, consignment number etc.). - /// In a case of direct capture, both fund reservation and capture are executed in a single operation. - /// It is important to check the response, and the capture is only successful when the response is HTTP 200 OK. - /// </summary> - /// <returns></returns> - /// <exception cref="HttpRequestException">Throws if the api returns unsuccessfully</exception> - public async Task<VippsPaymentActionResponse> CapturePaymentAsync( - string orderId, - VippsPaymentActionRequest payload, - CancellationToken ct = default - ) { - if (_client.DefaultRequestHeaders.Authorization?.Parameter.IsNullOrWhiteSpace() ?? true) { - var credentials = await GetAuthorizationTokenAsync(false, ct); - _client.DefaultRequestHeaders.Authorization = - new AuthenticationHeaderValue("Bearer", credentials.AccessToken); + try { + exception.ErrorResponse = + await response.Content.ReadFromJsonAsync<VippsErrorResponse>(cancellationToken: ct); + _logger.LogError(nameof(InitiatePaymentAsync) + + " Api error response: " + + JsonSerializer.Serialize(response.Content)); + } catch { + // ignored } + throw exception; + } + } - if (payload.MerchantInfo?.MerchantSerialNumber.IsNullOrWhiteSpace() ?? false) { - payload.MerchantInfo = new TMerchantInfoPayment { - MerchantSerialNumber = _vippsMsn - }; - } + /// <summary> + /// This API call allows merchant to capture the reserved amount. + /// Amount to capture cannot be higher than reserved. + /// The API also allows capturing partial amount of the reserved amount. + /// Partial capture can be called as many times as required so long there is reserved amount to capture. + /// Transaction text is not optional and is used as a proof of delivery (tracking code, consignment number etc.). + /// In a case of direct capture, both fund reservation and capture are executed in a single operation. + /// It is important to check the response, and the capture is only successful when the response is HTTP 200 OK. + /// </summary> + /// <returns></returns> + /// <exception cref="HttpRequestException">Throws if the api returns unsuccessfully</exception> + public async Task<VippsPaymentActionResponse> CapturePaymentAsync( + string orderId, + VippsPaymentActionRequest payload, + CancellationToken ct = default + ) { + if (_client.DefaultRequestHeaders.Authorization?.Parameter.IsNullOrWhiteSpace() ?? true) { + var credentials = await GetAuthorizationTokenAsync(false, ct); + _client.DefaultRequestHeaders.Authorization = + new AuthenticationHeaderValue("Bearer", credentials.AccessToken); + } - var response = await _client.PostAsJsonAsync("ecomm/v2/payments/" + orderId + "/capture", - payload, - _requestJsonSerializerOptions, - ct); - try { - response.EnsureSuccessStatusCode(); - _logger.LogDebug(nameof(VippsEcommerceService) - + ": Successfully issued a request to " - + response.RequestMessage?.RequestUri); - return await response.Content.ReadFromJsonAsync<VippsPaymentActionResponse>(cancellationToken: ct); - } catch (Exception e) { - var exception = new VippsRequestException(nameof(CapturePaymentAsync) + " failed.", e); - if (e is not HttpRequestException) { - throw exception; - } + if (payload.MerchantInfo?.MerchantSerialNumber.IsNullOrWhiteSpace() ?? false) { + payload.MerchantInfo = new TMerchantInfoPayment { + MerchantSerialNumber = _vippsMsn + }; + } - try { - exception.ErrorResponse = - await response.Content.ReadFromJsonAsync<VippsErrorResponse>(cancellationToken: ct); - _logger.LogError(nameof(CapturePaymentAsync) - + " Api error response: " - + JsonSerializer.Serialize(response.Content)); - } catch { - // ignored - } + var response = await _client.PostAsJsonAsync("ecomm/v2/payments/" + orderId + "/capture", + payload, + _requestJsonSerializerOptions, + ct); + try { + response.EnsureSuccessStatusCode(); + _logger.LogDebug(nameof(VippsEcommerceService) + + ": Successfully issued a request to " + + response.RequestMessage?.RequestUri); + return await response.Content.ReadFromJsonAsync<VippsPaymentActionResponse>(cancellationToken: ct); + } catch (Exception e) { + var exception = new VippsRequestException(nameof(CapturePaymentAsync) + " failed.", e); + if (e is not HttpRequestException) { throw exception; } - } - - /// <summary> - /// The API call allows merchant to cancel the reserved or initiated transaction. - /// The API will not allow partial cancellation which has the consequence that partially captured transactions cannot be cancelled. - /// Please note that in a case of communication errors during initiate payment service call between Vipps and PSP/Acquirer/Issuer; even in a case that customer has confirmed a payment, the payment will be cancelled by Vipps. - /// Note this means you can not cancel a captured payment. - /// </summary> - /// <returns></returns> - /// <exception cref="HttpRequestException">Throws if the api returns unsuccessfully</exception> - public async Task<VippsPaymentActionResponse> CancelPaymentAsync( - string orderId, - VippsPaymentActionRequest payload, - CancellationToken ct = default - ) { - if (_client.DefaultRequestHeaders.Authorization?.Parameter.IsNullOrWhiteSpace() ?? true) { - var credentials = await GetAuthorizationTokenAsync(false, ct); - _client.DefaultRequestHeaders.Authorization = - new AuthenticationHeaderValue("Bearer", credentials.AccessToken); + try { + exception.ErrorResponse = + await response.Content.ReadFromJsonAsync<VippsErrorResponse>(cancellationToken: ct); + _logger.LogError(nameof(CapturePaymentAsync) + + " Api error response: " + + JsonSerializer.Serialize(response.Content)); + } catch { + // ignored } - if (payload.MerchantInfo?.MerchantSerialNumber.IsNullOrWhiteSpace() ?? false) { - payload.MerchantInfo = new TMerchantInfoPayment { - MerchantSerialNumber = _vippsMsn - }; - } + throw exception; + } + } - var response = await _client.PutAsJsonAsync("ecomm/v2/payments/" + orderId + "/cancel", - payload, - _requestJsonSerializerOptions, - ct); - try { - response.EnsureSuccessStatusCode(); - _logger.LogDebug(nameof(VippsEcommerceService) - + ": Successfully issued a request to " - + response.RequestMessage?.RequestUri); - return await response.Content.ReadFromJsonAsync<VippsPaymentActionResponse>(cancellationToken: ct); - } catch (Exception e) { - var exception = new VippsRequestException(nameof(CancelPaymentAsync) + " failed.", e); - if (e is not HttpRequestException) { - throw exception; - } + /// <summary> + /// The API call allows merchant to cancel the reserved or initiated transaction. + /// The API will not allow partial cancellation which has the consequence that partially captured transactions cannot be cancelled. + /// Please note that in a case of communication errors during initiate payment service call between Vipps and PSP/Acquirer/Issuer; even in a case that customer has confirmed a payment, the payment will be cancelled by Vipps. + /// Note this means you can not cancel a captured payment. + /// </summary> + /// <returns></returns> + /// <exception cref="HttpRequestException">Throws if the api returns unsuccessfully</exception> + public async Task<VippsPaymentActionResponse> CancelPaymentAsync( + string orderId, + VippsPaymentActionRequest payload, + CancellationToken ct = default + ) { + if (_client.DefaultRequestHeaders.Authorization?.Parameter.IsNullOrWhiteSpace() ?? true) { + var credentials = await GetAuthorizationTokenAsync(false, ct); + _client.DefaultRequestHeaders.Authorization = + new AuthenticationHeaderValue("Bearer", credentials.AccessToken); + } - try { - exception.ErrorResponse = - await response.Content.ReadFromJsonAsync<VippsErrorResponse>(cancellationToken: ct); - _logger.LogError(nameof(CancelPaymentAsync) - + " Api error response: " - + JsonSerializer.Serialize(response.Content)); - } catch { - // ignored - } + if (payload.MerchantInfo?.MerchantSerialNumber.IsNullOrWhiteSpace() ?? false) { + payload.MerchantInfo = new TMerchantInfoPayment { + MerchantSerialNumber = _vippsMsn + }; + } + + var response = await _client.PutAsJsonAsync("ecomm/v2/payments/" + orderId + "/cancel", + payload, + _requestJsonSerializerOptions, + ct); + try { + response.EnsureSuccessStatusCode(); + _logger.LogDebug(nameof(VippsEcommerceService) + + ": Successfully issued a request to " + + response.RequestMessage?.RequestUri); + return await response.Content.ReadFromJsonAsync<VippsPaymentActionResponse>(cancellationToken: ct); + } catch (Exception e) { + var exception = new VippsRequestException(nameof(CancelPaymentAsync) + " failed.", e); + if (e is not HttpRequestException) { throw exception; } - } - /// <summary> - /// The API call allows merchant to refresh the authorizations of the payment. - /// A reservation's lifetime is defined by the scheme. Typically 7 days for Visa, and 30 days for Mastercard. - /// This is currently not live in production and will be added shortly. - /// </summary> - /// <returns></returns> - /// <exception cref="HttpRequestException">Throws if the api returns unsuccessfully</exception> - public async Task<VippsPaymentActionResponse> AuthorizePaymentAsync( - string orderId, - VippsPaymentActionRequest payload, - CancellationToken ct = default - ) { - if (_client.DefaultRequestHeaders.Authorization?.Parameter.IsNullOrWhiteSpace() ?? true) { - var credentials = await GetAuthorizationTokenAsync(false, ct); - _client.DefaultRequestHeaders.Authorization = - new AuthenticationHeaderValue("Bearer", credentials.AccessToken); + try { + exception.ErrorResponse = + await response.Content.ReadFromJsonAsync<VippsErrorResponse>(cancellationToken: ct); + _logger.LogError(nameof(CancelPaymentAsync) + + " Api error response: " + + JsonSerializer.Serialize(response.Content)); + } catch { + // ignored } - if (payload.MerchantInfo?.MerchantSerialNumber.IsNullOrWhiteSpace() ?? false) { - payload.MerchantInfo = new TMerchantInfoPayment { - MerchantSerialNumber = _vippsMsn - }; - } + throw exception; + } + } - var response = await _client.PutAsJsonAsync("ecomm/v2/payments/" + orderId + "/authorize", - payload, - _requestJsonSerializerOptions, - ct); + /// <summary> + /// The API call allows merchant to refresh the authorizations of the payment. + /// A reservation's lifetime is defined by the scheme. Typically 7 days for Visa, and 30 days for Mastercard. + /// This is currently not live in production and will be added shortly. + /// </summary> + /// <returns></returns> + /// <exception cref="HttpRequestException">Throws if the api returns unsuccessfully</exception> + public async Task<VippsPaymentActionResponse> AuthorizePaymentAsync( + string orderId, + VippsPaymentActionRequest payload, + CancellationToken ct = default + ) { + if (_client.DefaultRequestHeaders.Authorization?.Parameter.IsNullOrWhiteSpace() ?? true) { + var credentials = await GetAuthorizationTokenAsync(false, ct); + _client.DefaultRequestHeaders.Authorization = + new AuthenticationHeaderValue("Bearer", credentials.AccessToken); + } - try { - response.EnsureSuccessStatusCode(); - _logger.LogDebug(nameof(VippsEcommerceService) - + ": Successfully issued a request to " - + response.RequestMessage?.RequestUri); - return await response.Content.ReadFromJsonAsync<VippsPaymentActionResponse>(cancellationToken: ct); - } catch (Exception e) { - var exception = new VippsRequestException(nameof(AuthorizePaymentAsync) + " failed.", e); - if (e is not HttpRequestException) { - throw exception; - } + if (payload.MerchantInfo?.MerchantSerialNumber.IsNullOrWhiteSpace() ?? false) { + payload.MerchantInfo = new TMerchantInfoPayment { + MerchantSerialNumber = _vippsMsn + }; + } - try { - exception.ErrorResponse = - await response.Content.ReadFromJsonAsync<VippsErrorResponse>(cancellationToken: ct); - _logger.LogError(nameof(AuthorizePaymentAsync) - + " Api error response: " - + JsonSerializer.Serialize(response.Content)); - } catch { - // ignored - } + var response = await _client.PutAsJsonAsync("ecomm/v2/payments/" + orderId + "/authorize", + payload, + _requestJsonSerializerOptions, + ct); + try { + response.EnsureSuccessStatusCode(); + _logger.LogDebug(nameof(VippsEcommerceService) + + ": Successfully issued a request to " + + response.RequestMessage?.RequestUri); + return await response.Content.ReadFromJsonAsync<VippsPaymentActionResponse>(cancellationToken: ct); + } catch (Exception e) { + var exception = new VippsRequestException(nameof(AuthorizePaymentAsync) + " failed.", e); + if (e is not HttpRequestException) { throw exception; } - } - /// <summary> - /// The API allows a merchant to do a refund of already captured transaction. - /// There is an option to do a partial refund of the captured amount. - /// Refunded amount cannot be larger than captured. - /// Timeframe for issuing a refund for a payment is 365 days from the date payment has been captured. - /// If the refund payment service call is called after the refund timeframe, service call will respond with an error. - /// Refunded funds will be transferred from the merchant account to the customer credit card that was used in payment flow. - /// Pay attention that in order to perform refund, there must be enough funds at merchant settlements account. - /// </summary> - /// <returns></returns> - /// <exception cref="HttpRequestException">Throws if the api returns unsuccessfully</exception> - public async Task<VippsPaymentActionResponse> RefundPaymentAsync( - string orderId, - VippsPaymentActionRequest payload, - CancellationToken ct = default - ) { - if (_client.DefaultRequestHeaders.Authorization?.Parameter.IsNullOrWhiteSpace() ?? true) { - var credentials = await GetAuthorizationTokenAsync(false, ct); - _client.DefaultRequestHeaders.Authorization = - new AuthenticationHeaderValue("Bearer", credentials.AccessToken); + try { + exception.ErrorResponse = + await response.Content.ReadFromJsonAsync<VippsErrorResponse>(cancellationToken: ct); + _logger.LogError(nameof(AuthorizePaymentAsync) + + " Api error response: " + + JsonSerializer.Serialize(response.Content)); + } catch { + // ignored } - if (payload.MerchantInfo?.MerchantSerialNumber.IsNullOrWhiteSpace() ?? false) { - payload.MerchantInfo = new TMerchantInfoPayment { - MerchantSerialNumber = _vippsMsn - }; - } + throw exception; + } + } - var response = await _client.PostAsJsonAsync("ecomm/v2/payments/" + orderId + "/refund", - payload, - _requestJsonSerializerOptions, - ct); - try { - response.EnsureSuccessStatusCode(); - _logger.LogDebug(nameof(VippsEcommerceService) - + ": Successfully issued a request to " - + response.RequestMessage?.RequestUri); - return await response.Content.ReadFromJsonAsync<VippsPaymentActionResponse>(cancellationToken: ct); - } catch (Exception e) { - var exception = new VippsRequestException(nameof(RefundPaymentAsync) + " failed.", e); - if (e is not HttpRequestException) { - throw exception; - } + /// <summary> + /// The API allows a merchant to do a refund of already captured transaction. + /// There is an option to do a partial refund of the captured amount. + /// Refunded amount cannot be larger than captured. + /// Timeframe for issuing a refund for a payment is 365 days from the date payment has been captured. + /// If the refund payment service call is called after the refund timeframe, service call will respond with an error. + /// Refunded funds will be transferred from the merchant account to the customer credit card that was used in payment flow. + /// Pay attention that in order to perform refund, there must be enough funds at merchant settlements account. + /// </summary> + /// <returns></returns> + /// <exception cref="HttpRequestException">Throws if the api returns unsuccessfully</exception> + public async Task<VippsPaymentActionResponse> RefundPaymentAsync( + string orderId, + VippsPaymentActionRequest payload, + CancellationToken ct = default + ) { + if (_client.DefaultRequestHeaders.Authorization?.Parameter.IsNullOrWhiteSpace() ?? true) { + var credentials = await GetAuthorizationTokenAsync(false, ct); + _client.DefaultRequestHeaders.Authorization = + new AuthenticationHeaderValue("Bearer", credentials.AccessToken); + } - try { - exception.ErrorResponse = - await response.Content.ReadFromJsonAsync<VippsErrorResponse>(cancellationToken: ct); - _logger.LogError(nameof(RefundPaymentAsync) - + " Api error response: " - + JsonSerializer.Serialize(response.Content)); - } catch { - // ignored - } + if (payload.MerchantInfo?.MerchantSerialNumber.IsNullOrWhiteSpace() ?? false) { + payload.MerchantInfo = new TMerchantInfoPayment { + MerchantSerialNumber = _vippsMsn + }; + } + var response = await _client.PostAsJsonAsync("ecomm/v2/payments/" + orderId + "/refund", + payload, + _requestJsonSerializerOptions, + ct); + try { + response.EnsureSuccessStatusCode(); + _logger.LogDebug(nameof(VippsEcommerceService) + + ": Successfully issued a request to " + + response.RequestMessage?.RequestUri); + return await response.Content.ReadFromJsonAsync<VippsPaymentActionResponse>(cancellationToken: ct); + } catch (Exception e) { + var exception = new VippsRequestException(nameof(RefundPaymentAsync) + " failed.", e); + if (e is not HttpRequestException) { throw exception; } - } - - /// <summary> - /// This endpoint allows developers to approve a payment through the Vipps eCom API without the use of the Vipps app. - /// This is useful for automated testing. - /// Express checkout is not supported for this endpoint. - /// The endpoint is only available in our Test environment. - /// Attempted use of the endpoint in production is not allowed, and will fail. - /// </summary> - /// <returns></returns> - /// <exception cref="HttpRequestException">Throws if the api returns unsuccessfully</exception> - public async Task<bool> ForceApprovePaymentAsync( - string orderId, - VippsForceApproveRequest payload, - CancellationToken ct = default - ) { - if (_client.DefaultRequestHeaders.Authorization?.Parameter.IsNullOrWhiteSpace() ?? true) { - var credentials = await GetAuthorizationTokenAsync(false, ct); - _client.DefaultRequestHeaders.Authorization = - new AuthenticationHeaderValue("Bearer", credentials.AccessToken); + try { + exception.ErrorResponse = + await response.Content.ReadFromJsonAsync<VippsErrorResponse>(cancellationToken: ct); + _logger.LogError(nameof(RefundPaymentAsync) + + " Api error response: " + + JsonSerializer.Serialize(response.Content)); + } catch { + // ignored } + throw exception; + } + } - var response = - await _client.PostAsJsonAsync("ecomm/v2/integration-test/payments/" + orderId + "/approve", - payload, - _requestJsonSerializerOptions, - ct); - try { - response.EnsureSuccessStatusCode(); - _logger.LogDebug(nameof(VippsEcommerceService) - + ": Successfully issued a request to " - + response.RequestMessage?.RequestUri); - return true; - } catch (Exception e) { - var exception = new VippsRequestException(nameof(ForceApprovePaymentAsync) + " failed.", e); - if (e is not HttpRequestException) { - throw exception; - } + /// <summary> + /// This endpoint allows developers to approve a payment through the Vipps eCom API without the use of the Vipps app. + /// This is useful for automated testing. + /// Express checkout is not supported for this endpoint. + /// The endpoint is only available in our Test environment. + /// Attempted use of the endpoint in production is not allowed, and will fail. + /// </summary> + /// <returns></returns> + /// <exception cref="HttpRequestException">Throws if the api returns unsuccessfully</exception> + public async Task<bool> ForceApprovePaymentAsync( + string orderId, + VippsForceApproveRequest payload, + CancellationToken ct = default + ) { + if (_client.DefaultRequestHeaders.Authorization?.Parameter.IsNullOrWhiteSpace() ?? true) { + var credentials = await GetAuthorizationTokenAsync(false, ct); + _client.DefaultRequestHeaders.Authorization = + new AuthenticationHeaderValue("Bearer", credentials.AccessToken); + } - try { - exception.ErrorResponse = - await response.Content.ReadFromJsonAsync<VippsErrorResponse>(cancellationToken: ct); - _logger.LogError(nameof(ForceApprovePaymentAsync) - + " Api error response: " - + JsonSerializer.Serialize(response.Content)); - } catch { - // ignored - } + var response = + await _client.PostAsJsonAsync("ecomm/v2/integration-test/payments/" + orderId + "/approve", + payload, + _requestJsonSerializerOptions, + ct); + + try { + response.EnsureSuccessStatusCode(); + _logger.LogDebug(nameof(VippsEcommerceService) + + ": Successfully issued a request to " + + response.RequestMessage?.RequestUri); + return true; + } catch (Exception e) { + var exception = new VippsRequestException(nameof(ForceApprovePaymentAsync) + " failed.", e); + if (e is not HttpRequestException) { throw exception; } - } - /// <summary> - /// This API call allows merchant to get the details of a payment transaction. - /// Service call returns detailed transaction history of given payment where events are sorted from newest to oldest for when the transaction occurred. - /// </summary> - /// <returns></returns> - /// <exception cref="HttpRequestException">Throws if the api returns unsuccessfully</exception> - public async Task<VippsGetPaymentDetailsResponse> GetPaymentDetailsAsync( - string orderId, - CancellationToken ct = default - ) { - if (_client.DefaultRequestHeaders.Authorization?.Parameter.IsNullOrWhiteSpace() ?? true) { - var credentials = await GetAuthorizationTokenAsync(false, ct); - _client.DefaultRequestHeaders.Authorization = - new AuthenticationHeaderValue("Bearer", credentials.AccessToken); + try { + exception.ErrorResponse = + await response.Content.ReadFromJsonAsync<VippsErrorResponse>(cancellationToken: ct); + _logger.LogError(nameof(ForceApprovePaymentAsync) + + " Api error response: " + + JsonSerializer.Serialize(response.Content)); + } catch { + // ignored } - var response = await _client.GetAsync("ecomm/v2/payments/" + orderId + "/details", ct); + throw exception; + } + } - try { - response.EnsureSuccessStatusCode(); - _logger.LogDebug(nameof(VippsEcommerceService) - + ": Successfully issued a request to " - + response.RequestMessage?.RequestUri); - return await - response.Content.ReadFromJsonAsync<VippsGetPaymentDetailsResponse>(cancellationToken: ct); - } catch (Exception e) { - var exception = new VippsRequestException(nameof(GetPaymentDetailsAsync) + " failed.", e); - if (e is not HttpRequestException) { - throw exception; - } + /// <summary> + /// This API call allows merchant to get the details of a payment transaction. + /// Service call returns detailed transaction history of given payment where events are sorted from newest to oldest for when the transaction occurred. + /// </summary> + /// <returns></returns> + /// <exception cref="HttpRequestException">Throws if the api returns unsuccessfully</exception> + public async Task<VippsGetPaymentDetailsResponse> GetPaymentDetailsAsync( + string orderId, + CancellationToken ct = default + ) { + if (_client.DefaultRequestHeaders.Authorization?.Parameter.IsNullOrWhiteSpace() ?? true) { + var credentials = await GetAuthorizationTokenAsync(false, ct); + _client.DefaultRequestHeaders.Authorization = + new AuthenticationHeaderValue("Bearer", credentials.AccessToken); + } - try { - exception.ErrorResponse = - await response.Content.ReadFromJsonAsync<VippsErrorResponse>(cancellationToken: ct); - _logger.LogError(nameof(GetPaymentDetailsAsync) - + " Api error response: " - + JsonSerializer.Serialize(response.Content)); - } catch { - // ignored - } + var response = await _client.GetAsync("ecomm/v2/payments/" + orderId + "/details", ct); + try { + response.EnsureSuccessStatusCode(); + _logger.LogDebug(nameof(VippsEcommerceService) + + ": Successfully issued a request to " + + response.RequestMessage?.RequestUri); + return await + response.Content.ReadFromJsonAsync<VippsGetPaymentDetailsResponse>(cancellationToken: ct); + } catch (Exception e) { + var exception = new VippsRequestException(nameof(GetPaymentDetailsAsync) + " failed.", e); + if (e is not HttpRequestException) { throw exception; } + + try { + exception.ErrorResponse = + await response.Content.ReadFromJsonAsync<VippsErrorResponse>(cancellationToken: ct); + _logger.LogError(nameof(GetPaymentDetailsAsync) + + " Api error response: " + + JsonSerializer.Serialize(response.Content)); + } catch { + // ignored + } + + throw exception; } } } |
