Importing UI for registering and verifying Shopify credentials

This commit is contained in:
rockstardev 2020-09-13 17:12:42 -05:00 committed by Kukks
parent cf99f0fca0
commit 8a68e1b49d
5 changed files with 230 additions and 12 deletions

View file

@ -20,6 +20,7 @@ using BTCPayServer.Services;
using BTCPayServer.Services.Apps;
using BTCPayServer.Services.Invoices;
using BTCPayServer.Services.Rates;
using BTCPayServer.Services.Shopify;
using BTCPayServer.Services.Stores;
using BTCPayServer.Services.Wallets;
using Microsoft.AspNetCore.Authorization;
@ -964,5 +965,89 @@ namespace BTCPayServer.Controllers
});
}
//
[HttpGet]
[Route("{storeId}/integrations")]
public async Task<IActionResult> Integrations()
{
var blob = CurrentStore.GetStoreBlob();
var vm = new IntegrationsViewModel
{
Shopify = blob.Shopify
};
return View("Integrations", vm);
}
[HttpPost]
[Route("{storeId}/integrations")]
public async Task<IActionResult> Integrations([FromServices] IHttpClientFactory clientFactory,
IntegrationsViewModel vm, string command = "")
{
if (command == "ShopifySaveCredentials")
{
var shopify = vm.Shopify;
var validCreds = shopify != null && shopify?.CredentialsPopulated() == true;
if (!validCreds)
{
TempData[WellKnownTempData.ErrorMessage] = "Please provide valid Shopify credentials";
//
return View("Integrations", vm);
}
var apiCreds = new ShopifyApiClientCredentials
{
ShopName = shopify.ShopName,
ApiKey = shopify.ApiKey,
ApiPassword = shopify.Password,
SharedSecret = shopify.SharedSecret
};
var apiClient = new ShopifyApiClient(clientFactory, null, apiCreds);
try
{
var result = await apiClient.OrdersCount();
}
catch
{
TempData[WellKnownTempData.ErrorMessage] = "Shopify rejected provided credentials, please correct values and again";
//
return View("Integrations", vm);
}
shopify.CredentialsValid = true;
var blob = CurrentStore.GetStoreBlob();
blob.Shopify = shopify;
if (CurrentStore.SetStoreBlob(blob))
{
await _Repo.UpdateStore(CurrentStore);
}
TempData[WellKnownTempData.SuccessMessage] = "Shopify credentials successfully updated";
}
else if (command == "ShopifyIntegrate")
{
var shopify = vm.Shopify;
var blob = CurrentStore.GetStoreBlob();
blob.Shopify.IntegratedAt = DateTimeOffset.UtcNow;
if (CurrentStore.SetStoreBlob(blob))
{
await _Repo.UpdateStore(CurrentStore);
}
TempData[WellKnownTempData.SuccessMessage] = "Shopify integration successfully turned on";
}
return RedirectToAction(nameof(Integrations), new
{
storeId = CurrentStore.Id
});
}
}
}

View file

@ -26,6 +26,28 @@ namespace BTCPayServer.Data
RecommendedFeeBlockTarget = 1;
}
public ShopifyDataHolder Shopify { get; set; }
public class ShopifyDataHolder
{
public string ShopName { get; set; }
public string ApiKey { get; set; }
public string Password { get; set; }
public string SharedSecret { get; set; }
public bool CredentialsPopulated()
{
return
!String.IsNullOrWhiteSpace(ShopName) &&
!String.IsNullOrWhiteSpace(ApiKey) &&
!String.IsNullOrWhiteSpace(Password) &&
!String.IsNullOrWhiteSpace(SharedSecret);
}
public bool CredentialsValid { get; set; }
public DateTimeOffset? IntegratedAt { get; set; }
}
[Obsolete("Use NetworkFeeMode instead")]
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? NetworkFeeDisabled

View file

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static BTCPayServer.Data.StoreBlob;
namespace BTCPayServer.Models.StoreViewModels
{
public class IntegrationsViewModel
{
public ShopifyDataHolder Shopify { get; set; }
}
}

View file

@ -36,6 +36,23 @@ namespace BTCPayServer.Services.Shopify
_httpClient.DefaultRequestHeaders.Add("Authorization", "Basic " + bearer);
}
private HttpRequestMessage createRequest(string shopNameInUrl, HttpMethod method, string action)
{
var url = $"https://{shopNameInUrl}.myshopify.com/admin/api/2020-07/" + action;
var req = new HttpRequestMessage(method, url);
return req;
}
private async Task<string> sendRequest(HttpRequestMessage req)
{
using var resp = await _httpClient.SendAsync(req);
var strResp = await resp.Content.ReadAsStringAsync();
return strResp;
}
public async Task<dynamic> TransactionsList(string orderId)
{
var req = createRequest(_creds.ShopName, HttpMethod.Get, $"orders/{orderId}/transactions.json");
@ -58,21 +75,14 @@ namespace BTCPayServer.Services.Shopify
return JObject.Parse(strResp);
}
private HttpRequestMessage createRequest(string shopNameInUrl, HttpMethod method, string action)
public async Task<int> OrdersCount()
{
var url = $"https://{shopNameInUrl}.myshopify.com/admin/api/2020-07/" + action;
var req = createRequest(_creds.ShopName, HttpMethod.Get, $"orders/count.json");
var strResp = await sendRequest(req);
var req = new HttpRequestMessage(method, url);
dynamic parsed = JObject.Parse(strResp);
return req;
}
private async Task<string> sendRequest(HttpRequestMessage req)
{
using var resp = await _httpClient.SendAsync(req);
var strResp = await resp.Content.ReadAsStringAsync();
return strResp;
return parsed.count;
}
}

View file

@ -0,0 +1,87 @@
@using static BTCPayServer.Data.StoreBlob
@model IntegrationsViewModel
@{
Layout = "../Shared/_NavLayout.cshtml";
ViewData.SetActivePageAndTitle(StoreNavPages.Integrations, "Integrations");
}
<partial name="_StatusMessage" />
@if (!ViewContext.ModelState.IsValid)
{
<div class="row">
<div class="col-md-6">
<div asp-validation-summary="All" class="text-danger"></div>
</div>
</div>
}
<div class="row">
<div class="col-md-8">
<form method="post">
<h4 class="mb-3">Shopify</h4>
<div class="form-group">
<label asp-for="Shopify.ShopName"></label>
<a href="https://docs.btcpayserver.org/Theme/#checkout-page-themes" target="_blank"><span class="fa fa-question-circle-o" title="More information..."></span></a>
<input asp-for="Shopify.ShopName" class="form-control" />
<span asp-validation-for="Shopify.ShopName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Shopify.ApiKey"></label>
<a href="https://docs.btcpayserver.org/Theme/#checkout-page-themes" target="_blank"><span class="fa fa-question-circle-o" title="More information..."></span></a>
<input asp-for="Shopify.ApiKey" class="form-control" />
<span asp-validation-for="Shopify.ApiKey" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Shopify.Password"></label>
<a href="https://docs.btcpayserver.org/Theme/#checkout-page-themes" target="_blank"><span class="fa fa-question-circle-o" title="More information..."></span></a>
<input asp-for="Shopify.Password" class="form-control" />
<span asp-validation-for="Shopify.Password" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Shopify.SharedSecret"></label>
<a href="https://docs.btcpayserver.org/Theme/#checkout-page-themes" target="_blank"><span class="fa fa-question-circle-o" title="More information..."></span></a>
<input asp-for="Shopify.SharedSecret" class="form-control" />
<span asp-validation-for="Shopify.SharedSecret" class="text-danger"></span>
</div>
@if (Model.Shopify?.CredentialsValid == false)
{
<button name="command" type="submit" class="btn btn-primary" value="ShopifySaveCredentials">Save Credentials</button>
}
else
{
<button name="command" type="submit" class="btn btn-error btn-sm" value="ShopifyClearCredentials">Clear Credentials</button>
}
@if (Model.Shopify?.CredentialsValid == true)
{
var shopify = Model.Shopify;
<br /><br />
<h4 class="mb-3">Shopify Operations</h4>
if (!shopify.IntegratedAt.HasValue)
{
<button name="command" type="submit" class="btn btn-primary" value="ShopifyIntegrate">Integrate Shopify Order Paid Marking</button>
}
else
{
<p>
Orders on <b>@shopify.ShopName</b>.myshopify.com will be marked as paid on successful invoice payment.
Started: @shopify.IntegratedAt.Value.ToBrowserDate()
</p>
<p></p>
}
}
</form>
</div>
</div>
@section Scripts {
@await Html.PartialAsync("_ValidationScriptsPartial")
}