Compare commits

...

1 Commits

Author SHA1 Message Date
Chukwuleta Tobechi
3c3abaf1e1 Configuration to handle restock when Invoice expires 2025-11-13 14:40:46 +01:00
7 changed files with 98 additions and 11 deletions

View File

@ -236,6 +236,55 @@ public class UIShopifyV2Controller : Controller
}
}
[HttpGet("~/stores/{storeId}/plugins/shopify-v2/configuration")]
public async Task<IActionResult> Configuration(string storeId)
{
var settings = await _storeRepo.GetSettingAsync<ShopifyStoreSettings>(storeId, ShopifyStoreSettings.SettingsName) ?? new();
if (settings.Setup is { DeployedCommit: null, AccessToken: null })
{
this.TempData.SetStatusMessageModel(new StatusMessageModel()
{
Message = "Kindly complete your Shopify setup",
Severity = StatusMessageModel.StatusSeverity.Error
});
return RedirectToAction(nameof(Index), new { storeId });
}
return View("/Views/UIShopify/Configuration.cshtml", new ShopifySettingsViewModel()
{
RestockOnInvoiceExpired = settings.RestockOnInvoiceExpired,
ClientId = settings.Setup?.ClientId,
ClientSecret = settings.Setup?.ClientSecret,
ShopUrl = settings.Setup?.ShopUrl,
ShopName = GetShopName(settings.Setup?.ShopUrl),
AppName = settings.PreferredAppName ?? ShopifyStoreSettings.DefaultAppName,
});
}
[HttpPost("~/stores/{storeId}/plugins/shopify-v2/configuration")]
public async Task<IActionResult> Configuration(ShopifySettingsViewModel model)
{
if (CurrentStore.Id == null) return NotFound();
var settings = await _storeRepo.GetSettingAsync<ShopifyStoreSettings>(CurrentStore.Id, ShopifyStoreSettings.SettingsName) ?? new();
if (settings.Setup is { DeployedCommit: null, AccessToken: null })
{
this.TempData.SetStatusMessageModel(new StatusMessageModel()
{
Message = "Kindly complete your Shopify setup",
Severity = StatusMessageModel.StatusSeverity.Error
});
return RedirectToAction(nameof(Settings), new { storeId = CurrentStore.Id });
}
settings.RestockOnInvoiceExpired = model.RestockOnInvoiceExpired;
await _storeRepo.UpdateSetting(CurrentStore.Id, ShopifyStoreSettings.SettingsName, settings);
this.TempData.SetStatusMessageModel(new StatusMessageModel()
{
Message = "Shopify configuration updated successfully",
Severity = StatusMessageModel.StatusSeverity.Success
});
return RedirectToAction(nameof(Settings), new { storeId = CurrentStore.Id });
}
static AsyncDuplicateLock OrderLocks = new AsyncDuplicateLock();
[AllowAnonymous]
[EnableCors(CorsPolicies.All)]

View File

@ -1,4 +1,8 @@
using BTCPayServer.Client.Models;
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Client.Models;
using BTCPayServer.Data;
using BTCPayServer.Events;
using BTCPayServer.HostedServices;
@ -6,27 +10,27 @@ using BTCPayServer.Logging;
using BTCPayServer.Plugins.ShopifyPlugin.Clients;
using BTCPayServer.Services.Invoices;
using BTCPayServer.Services.Rates;
using BTCPayServer.Services.Stores;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace BTCPayServer.Plugins.ShopifyPlugin.Services;
public class ShopifyHostedService : EventHostedServiceBase
{
private readonly StoreRepository _storeRepo;
private readonly CurrencyNameTable _currencyNameTable;
private readonly InvoiceRepository _invoiceRepository;
private readonly ShopifyClientFactory shopifyClientFactory;
public ShopifyHostedService(EventAggregator eventAggregator,
public ShopifyHostedService(StoreRepository storeRepo,
EventAggregator eventAggregator,
InvoiceRepository invoiceRepository,
CurrencyNameTable currencyNameTable,
ShopifyClientFactory shopifyClientFactory,
Logs logs) : base(eventAggregator, logs)
{
_storeRepo = storeRepo;
_currencyNameTable = currencyNameTable;
_invoiceRepository = invoiceRepository;
this.shopifyClientFactory = shopifyClientFactory;
@ -82,7 +86,8 @@ public class ShopifyHostedService : EventHostedServiceBase
{
var logs = new InvoiceLogs();
var client = await shopifyClientFactory.CreateAPIClient(invoice.StoreId);
if (client is null)
var shopifySettings = await _storeRepo.GetSettingAsync<ShopifyStoreSettings>(invoice.StoreId, ShopifyStoreSettings.SettingsName);
if (client is null || shopifySettings is null)
return logs;
if (await client.GetOrder(shopifyOrderId, true) is not { } order)
return logs;
@ -152,8 +157,8 @@ public class ShopifyHostedService : EventHostedServiceBase
OrderId = order.Id,
NotifyCustomer = false,
Reason = OrderCancelReason.DECLINED,
Restock = true,
Refund = false,
Restock = shopifySettings.RestockOnInvoiceExpired,
Refund = false,
StaffNote = $"BTCPay Invoice {invoice.Id} is {invoice.Status}"
});
logs.Write($"Shopify order cancelled. (Invoice Status: {invoice.Status})", InvoiceEventData.EventSeverity.Warning);

View File

@ -11,7 +11,8 @@ namespace BTCPayServer.Plugins.ShopifyPlugin
}
public const string SettingsName = "ShopifyPluginSettings";
public const string DefaultAppName = "BTCPay Server";
}
public bool RestockOnInvoiceExpired { get; set; } = true;
}
public class ShopifySetupSettings
{

View File

@ -26,5 +26,6 @@ namespace BTCPayServer.Plugins.ShopifyPlugin.ViewModels
public string CLIToken { get; set; }
public string ShopUrl { get; set; }
public string ShopName { get; set; }
public bool RestockOnInvoiceExpired { get; set; }
}
}

View File

@ -0,0 +1,27 @@
@using BTCPayServer.Abstractions.Extensions
@using BTCPayServer.Plugins.ShopifyPlugin.ViewModels
@using Microsoft.AspNetCore.Routing
@using BTCPayServer.Plugins.ShopifyPlugin.Blazor
@model ShopifySettingsViewModel
@{
ViewData.SetActivePage("Shopify", "Shopify App Configuration", "Shopify");
}
<form method="post" asp-action="Configuration" asp-controller="UIShopifyV2">
<div class="sticky-header d-flex align-items-center justify-content-between">
<h2>@ViewData["Title"]</h2>
<button type="submit" class="btn btn-primary" id="Edit" value="Save Changes">Save Changes</button>
</div>
<partial name="_StatusMessage" />
<div class="row">
<div class="col-xl-10 col-xxl-constrain">
<div class="form-group d-flex align-items-center">
<input asp-for="RestockOnInvoiceExpired" type="checkbox" class="btcpay-toggle me-3" />
<label asp-for="RestockOnInvoiceExpired" class="form-label mb-0 me-1">Restock product items when invoice expires</label>
<span asp-validation-for="RestockOnInvoiceExpired" class="text-danger"></span>
</div>
</div>
</div>
</form>

View File

@ -32,6 +32,10 @@
</small>
</h2>
<div>
@if (Model.Step == ShopifySettingsViewModel.State.Done)
{
<a asp-controller="UIShopifyV2" asp-action="Configuration" asp-route-storeId="@storeId" class="btn btn-primary">Configuration</a>
}
@if (Model.Step != ShopifySettingsViewModel.State.WaitingClientCreds)
{
<form method="post">

View File

@ -26,7 +26,7 @@ services:
- "host.docker.internal:host-gateway"
shopify-app-deployer:
image: btcpayserver/shopify-app-deployer:1.4
image: btcpayserver/shopify-app-deployer:1.5
restart: unless-stopped
init: true
expose: