Remove deprecated Shopify Scripts integration (#6608)
What changed, and why it matters
This commit removes the old Shopify integration from BTCPay Server entirely. It deletes all Shopify-related code, settings pages, API clients, and rate-limiting rules. There is no security patch here—this is a feature removal because Shopify deprecated the underlying technology.
No security action required. Users relying on the legacy Shopify integration should migrate to the supported Shopify V2 integration before the deprecation deadline.
Security signals we found
Large-scale deletion of a deprecated third-party integration
Removal of rate-limit zone for Shopify endpoint
No bug fix, input validation, authentication, or authorization changes
Evidence from the diff
The commit deletes the legacy Shopify Scripts integration, including the Shopify plugin, API models, controller, service, views, JavaScript checkout integration, and the associated rate-limit zone. The only non-deletion changes are removal of the Shopify rate-limit registration in Startup.cs and cleanup of translation strings and an unused using statement. No vulnerability is fixed; the change is driven by Shopify’s deprecation of Shopify Scripts.
Changed components
BTCPayServer/Plugins/ShopifyBTCPayServer/Hosting/Startup.csBTCPayServer/ZoneLimits.csBTCPayServer/Plugins/Translations/Translations.Default.csBTCPayServer/Security/GreenField/APIKeyRepository.csInspect captured patch +1 / −1309
diff --git a/BTCPayServer/Hosting/Startup.cs b/BTCPayServer/Hosting/Startup.cs
index 14c2745..0b49825 100644
--- a/BTCPayServer/Hosting/Startup.cs
+++ b/BTCPayServer/Hosting/Startup.cs
@@ -291,7 +291,6 @@ namespace BTCPayServer.Hosting
rateLimits.SetZone($"zone={ZoneLimits.PublicInvoices} rate=1000r/min burst=100 nodelay");
rateLimits.SetZone($"zone={ZoneLimits.Register} rate=1000r/min burst=100 nodelay");
rateLimits.SetZone($"zone={ZoneLimits.PayJoin} rate=1000r/min burst=100 nodelay");
- rateLimits.SetZone($"zone={ZoneLimits.Shopify} rate=1000r/min burst=100 nodelay");
rateLimits.SetZone($"zone={ZoneLimits.ForgotPassword} rate=5r/d burst=3 nodelay");
}
else
@@ -300,7 +299,6 @@ namespace BTCPayServer.Hosting
rateLimits.SetZone($"zone={ZoneLimits.PublicInvoices} rate=4r/min burst=10 delay=3");
rateLimits.SetZone($"zone={ZoneLimits.Register} rate=2r/min burst=2 nodelay");
rateLimits.SetZone($"zone={ZoneLimits.PayJoin} rate=5r/min burst=3 nodelay");
- rateLimits.SetZone($"zone={ZoneLimits.Shopify} rate=20r/min burst=3 nodelay");
rateLimits.SetZone($"zone={ZoneLimits.ForgotPassword} rate=5r/d burst=5 nodelay");
}
diff --git a/BTCPayServer/Plugins/Shopify/ApiModels/CreateScriptResponse.cs b/BTCPayServer/Plugins/Shopify/ApiModels/CreateScriptResponse.cs
deleted file mode 100644
index ce7ccb2..0000000
--- a/BTCPayServer/Plugins/Shopify/ApiModels/CreateScriptResponse.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-using System;
-using Newtonsoft.Json;
-
-namespace BTCPayServer.Plugins.Shopify.ApiModels
-{
- public class CreateScriptResponse
- {
- [JsonProperty("script_tag")]
- public ScriptTag ScriptTag { get; set; }
- }
-
- public class ScriptTag
- {
- [JsonProperty("id")]
- public int Id { get; set; }
-
- [JsonProperty("src")]
- public string Src { get; set; }
-
- [JsonProperty("event")]
- public string Event { get; set; }
-
- [JsonProperty("created_at")]
- public DateTime CreatedAt { get; set; }
-
- [JsonProperty("updated_at")]
- public DateTime UpdatedAt { get; set; }
-
- [JsonProperty("display_scope")]
- public string DisplayScope { get; set; }
- }
-}
diff --git a/BTCPayServer/Plugins/Shopify/ApiModels/CreateWebhookResponse.cs b/BTCPayServer/Plugins/Shopify/ApiModels/CreateWebhookResponse.cs
deleted file mode 100644
index e3e30b6..0000000
--- a/BTCPayServer/Plugins/Shopify/ApiModels/CreateWebhookResponse.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-using System;
-using System.Collections.Generic;
-using Newtonsoft.Json;
-
-namespace BTCPayServer.Plugins.Shopify.ApiModels
-{
- public class CreateWebhookResponse
- {
- [JsonProperty("webhook")] public Webhook Webhook { get; set; }
- }
-
- public class Webhook
- {
- [JsonProperty("id")] public int Id { get; set; }
-
- [JsonProperty("address")] public string Address { get; set; }
-
- [JsonProperty("topic")] public string Topic { get; set; }
-
- [JsonProperty("created_at")] public DateTime CreatedAt { get; set; }
-
- [JsonProperty("updated_at")] public DateTime UpdatedAt { get; set; }
-
- [JsonProperty("format")] public string Format { get; set; }
-
- [JsonProperty("fields")] public List<object> Fields { get; set; }
-
- [JsonProperty("metafield_namespaces")] public List<object> MetafieldNamespaces { get; set; }
-
- [JsonProperty("api_version")] public string ApiVersion { get; set; }
-
- [JsonProperty("private_metafield_namespaces")]
- public List<object> PrivateMetafieldNamespaces { get; set; }
- }
-}
diff --git a/BTCPayServer/Plugins/Shopify/ApiModels/DataHolders/TransactionDataHolder.cs b/BTCPayServer/Plugins/Shopify/ApiModels/DataHolders/TransactionDataHolder.cs
deleted file mode 100644
index 2091cd1..0000000
--- a/BTCPayServer/Plugins/Shopify/ApiModels/DataHolders/TransactionDataHolder.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using System;
-
-namespace BTCPayServer.Plugins.Shopify.ApiModels.DataHolders
-{
- public class TransactionDataHolder
- {
- public long id { get; set; }
- public long? order_id { get; set; }
- public string kind { get; set; }
- public string gateway { get; set; }
- public string status { get; set; }
- public string message { get; set; }
- public DateTimeOffset created_at { get; set; }
- public bool test { get; set; }
- public string authorization { get; set; }
- public string location_id { get; set; }
- public string user_id { get; set; }
- public long? parent_id { get; set; }
- public DateTimeOffset processed_at { get; set; }
- public string device_id { get; set; }
- public object receipt { get; set; }
- public string error_code { get; set; }
- public string source_name { get; set; }
- public string currency_exchange_adjustment { get; set; }
- public string amount { get; set; }
- public string currency { get; set; }
- public string admin_graphql_api_id { get; set; }
- }
-}
diff --git a/BTCPayServer/Plugins/Shopify/ApiModels/OrdersCountResp.cs b/BTCPayServer/Plugins/Shopify/ApiModels/OrdersCountResp.cs
deleted file mode 100644
index a0b85f8..0000000
--- a/BTCPayServer/Plugins/Shopify/ApiModels/OrdersCountResp.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-using Newtonsoft.Json;
-
-namespace BTCPayServer.Plugins.Shopify.ApiModels
-{
- public class CountResponse
- {
- [JsonProperty("count")]
- public long Count { get; set; }
- }
-}
diff --git a/BTCPayServer/Plugins/Shopify/ApiModels/ShopifyOrder.cs b/BTCPayServer/Plugins/Shopify/ApiModels/ShopifyOrder.cs
deleted file mode 100644
index 0d29f78..0000000
--- a/BTCPayServer/Plugins/Shopify/ApiModels/ShopifyOrder.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using System.Collections.Generic;
-using Newtonsoft.Json;
-
-namespace BTCPayServer.Plugins.Shopify.ApiModels
-{
- public class ShopifyOrder
- {
- [JsonProperty("id")]
- public long Id { get; set; }
- [JsonProperty("order_number")]
- public long OrderNumber { get; set; }
- [JsonProperty("total_price")]
- public decimal TotalPrice { get; set; }
- [JsonProperty("total_outstanding")]
- public decimal TotalOutstanding { get; set; }
- [JsonProperty("currency")]
- public string Currency { get; set; }
- [JsonProperty("presentment_currency")]
- public string PresentmentCurrency { get; set; }
- [JsonProperty("financial_status")]
- public string FinancialStatus { get; set; }
- [JsonProperty("transactions")]
- public IEnumerable<ShopifyTransaction> Transactions { get; set; }
- }
-}
diff --git a/BTCPayServer/Plugins/Shopify/ApiModels/ShopifyTransaction.cs b/BTCPayServer/Plugins/Shopify/ApiModels/ShopifyTransaction.cs
deleted file mode 100644
index dedfd40..0000000
--- a/BTCPayServer/Plugins/Shopify/ApiModels/ShopifyTransaction.cs
+++ /dev/null
@@ -1,48 +0,0 @@
-using System;
-using Newtonsoft.Json;
-
-namespace BTCPayServer.Plugins.Shopify.ApiModels
-{
- public class ShopifyTransaction
- {
- [JsonProperty("amount")]
- public decimal? Amount { get; set; }
-
- [JsonProperty("authorization")]
- public string Authorization { get; set; }
-
- [JsonProperty("created_at")]
- public DateTimeOffset? CreatedAt { get; set; }
-
- [JsonProperty("device_id")]
- public string DeviceId { get; set; }
-
- [JsonProperty("gateway")]
- public string Gateway { get; set; }
- [JsonProperty("kind")]
- public string Kind { get; set; }
- [JsonProperty("order_id")]
- public long? OrderId { get; set; }
-
- /// <summary>
- /// A standardized error code, e.g. 'incorrect_number', independent of the payment provider. Value can be null. A full list of known values can be found at https://help.shopify.com/api/reference/transaction.
- /// </summary>
- [JsonProperty("error_code")]
- public string ErrorCode { get; set; }
-
- /// <summary>
- /// The status of the transaction. Valid values are: pending, failure, success or error.
- /// </summary>
- [JsonProperty("status")]
- public string Status { get; set; }
- [JsonProperty("test")]
- public bool? Test { get; set; }
- [JsonProperty("currency")]
- public string Currency { get; set; }
- /// <summary>
- /// This property is undocumented by Shopify.
- /// </summary>
- [JsonProperty("parent_id")]
- public long? ParentId { get; set; }
- }
-}
diff --git a/BTCPayServer/Plugins/Shopify/ApiModels/TransactionsCreateReq.cs b/BTCPayServer/Plugins/Shopify/ApiModels/TransactionsCreateReq.cs
deleted file mode 100644
index 6b02dab..0000000
--- a/BTCPayServer/Plugins/Shopify/ApiModels/TransactionsCreateReq.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-namespace BTCPayServer.Plugins.Shopify.ApiModels
-{
- public class TransactionsCreateReq
- {
- public DataHolder transaction { get; set; }
-
- public class DataHolder
- {
- public string currency { get; set; }
- public string amount { get; set; }
- public string kind { get; set; }
- public long? parent_id { get; set; }
- public string gateway { get; set; }
- public string source { get; set; }
- public string status { get; set; }
- public string authorization { get; set; }
- }
- }
-}
diff --git a/BTCPayServer/Plugins/Shopify/ApiModels/TransactionsCreateResp.cs b/BTCPayServer/Plugins/Shopify/ApiModels/TransactionsCreateResp.cs
deleted file mode 100644
index 4111178..0000000
--- a/BTCPayServer/Plugins/Shopify/ApiModels/TransactionsCreateResp.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using BTCPayServer.Plugins.Shopify.ApiModels.DataHolders;
-
-namespace BTCPayServer.Plugins.Shopify.ApiModels
-{
- public class TransactionsCreateResp
- {
- public TransactionDataHolder transaction { get; set; }
- }
-}
diff --git a/BTCPayServer/Plugins/Shopify/ApiModels/TransactionsListResp.cs b/BTCPayServer/Plugins/Shopify/ApiModels/TransactionsListResp.cs
deleted file mode 100644
index 9b71c87..0000000
--- a/BTCPayServer/Plugins/Shopify/ApiModels/TransactionsListResp.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-using System.Collections.Generic;
-using BTCPayServer.Plugins.Shopify.ApiModels.DataHolders;
-
-namespace BTCPayServer.Plugins.Shopify.ApiModels
-{
- public class TransactionsListResp
- {
- public List<TransactionDataHolder> transactions { get; set; }
- }
-}
diff --git a/BTCPayServer/Plugins/Shopify/Models/ShopifySettings.cs b/BTCPayServer/Plugins/Shopify/Models/ShopifySettings.cs
deleted file mode 100644
index c8a4cf1..0000000
--- a/BTCPayServer/Plugins/Shopify/Models/ShopifySettings.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-using System;
-using System.ComponentModel.DataAnnotations;
-using Newtonsoft.Json;
-
-namespace BTCPayServer.Plugins.Shopify.Models
-{
- public class ShopifySettings
- {
- [Display(Name = "Shop Name")]
- public string ShopName { get; set; }
-
- [Display(Name = "API Key")]
- public string ApiKey { get; set; }
-
- [Display(Name = "Admin API access token")]
- public string Password { get; set; }
-
- public bool CredentialsPopulated()
- {
- return
- !string.IsNullOrWhiteSpace(ShopName) &&
- !string.IsNullOrWhiteSpace(ApiKey) &&
- !string.IsNullOrWhiteSpace(Password);
- }
- public DateTimeOffset? IntegratedAt { get; set; }
-
- [JsonIgnore]
- public string ShopifyUrl
- {
- get
- {
- return ShopName?.Contains('.', StringComparison.OrdinalIgnoreCase) is true ? ShopName : $"https://{ShopName}.myshopify.com";
- }
- }
- }
-}
diff --git a/BTCPayServer/Plugins/Shopify/ShopifyApiClient.cs b/BTCPayServer/Plugins/Shopify/ShopifyApiClient.cs
deleted file mode 100644
index fab51c6..0000000
--- a/BTCPayServer/Plugins/Shopify/ShopifyApiClient.cs
+++ /dev/null
@@ -1,152 +0,0 @@
-using System;
-using System.Linq;
-using System.Net.Http;
-using System.Text;
-using System.Threading.Tasks;
-using BTCPayServer.Plugins.Shopify.ApiModels;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
-
-namespace BTCPayServer.Plugins.Shopify
-{
- public class ShopifyApiClient
- {
- private readonly HttpClient _httpClient;
- private readonly ShopifyApiClientCredentials _credentials;
-
- public ShopifyApiClient(IHttpClientFactory httpClientFactory, ShopifyApiClientCredentials credentials)
- {
- if (httpClientFactory != null)
- {
- _httpClient = httpClientFactory.CreateClient(nameof(ShopifyApiClient));
- }
- else // tests don't provide IHttpClientFactory
- {
- _httpClient = new HttpClient();
- }
-
- _credentials = credentials;
-
- var bearer = $"{_credentials.ApiKey}:{_credentials.ApiPassword}";
- bearer = NBitcoin.DataEncoders.Encoders.Base64.EncodeData(Encoding.UTF8.GetBytes(bearer));
-
- _httpClient.DefaultRequestHeaders.Add("Authorization", "Basic " + bearer);
- }
-
- private HttpRequestMessage CreateRequest(string shopName, HttpMethod method, string action,
- string relativeUrl = null, string apiVersion = "2020-07")
- {
- var url =
- $"https://{(shopName.Contains('.', StringComparison.InvariantCulture) ? shopName : $"{shopName}.myshopify.com")}/{relativeUrl ?? ($"admin/api/{apiVersion}/" + 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();
- if (strResp.StartsWith("{", StringComparison.OrdinalIgnoreCase))
- {
- if (JObject.Parse(strResp)["errors"]?.Value<string>() is string error)
- {
- if (error == "Not Found")
- error = "Shop not found";
- throw new ShopifyApiException(error);
- }
- }
- return strResp;
- }
-
- public async Task<CreateWebhookResponse> CreateWebhook(string topic, string address, string format = "json")
- {
- var req = CreateRequest(_credentials.ShopName, HttpMethod.Post, $"webhooks.json");
- req.Content = new StringContent(JsonConvert.SerializeObject(new { topic, address, format }), Encoding.UTF8,
- "application/json");
- var strResp = await SendRequest(req);
-
- return JsonConvert.DeserializeObject<CreateWebhookResponse>(strResp);
- }
-
- public async Task RemoveWebhook(string id)
- {
- var req = CreateRequest(_credentials.ShopName, HttpMethod.Delete, $"webhooks/{id}.json");
- await SendRequest(req);
- }
-
- public async Task<string[]> CheckScopes()
- {
- var req = CreateRequest(_credentials.ShopName, HttpMethod.Get, null, "admin/oauth/access_scopes.json");
- var c = JObject.Parse(await SendRequest(req));
- return c["access_scopes"].Values<JToken>()
- .Select(token => token["handle"].Value<string>()).ToArray();
- }
-
- public async Task<TransactionsListResp> TransactionsList(string orderId)
- {
- var req = CreateRequest(_credentials.ShopName, HttpMethod.Get, $"orders/{orderId}/transactions.json");
-
- var strResp = await SendRequest(req);
-
- var parsed = JsonConvert.DeserializeObject<TransactionsListResp>(strResp);
-
- return parsed;
- }
-
- public async Task<TransactionsCreateResp> TransactionCreate(string orderId, TransactionsCreateReq txnCreate)
- {
- var postJson = JsonConvert.SerializeObject(txnCreate);
-
- var req = CreateRequest(_credentials.ShopName, HttpMethod.Post, $"orders/{orderId}/transactions.json");
- req.Content = new StringContent(postJson, Encoding.UTF8, "application/json");
-
- var strResp = await SendRequest(req);
- return JsonConvert.DeserializeObject<TransactionsCreateResp>(strResp);
- }
-
- public async Task<ShopifyOrder> GetOrder(string orderId)
- {
- var req = CreateRequest(_credentials.ShopName, HttpMethod.Get,
- $"orders/{orderId}.json?fields=id,order_number,total_price,total_outstanding,currency,presentment_currency,transactions,financial_status");
-
- var strResp = await SendRequest(req);
-
- return JObject.Parse(strResp)["order"].ToObject<ShopifyOrder>();
- }
- public async Task<ShopifyOrder> CancelOrder(string orderId)
- {
- var req = CreateRequest(_credentials.ShopName, HttpMethod.Post,
- $"orders/{orderId}/cancel.json?restock=true", null, "2024-04");
-
- var strResp = await SendRequest(req);
-
- return JObject.Parse(strResp)["order"].ToObject<ShopifyOrder>();
- }
-
- public async Task<long> OrdersCount()
- {
- var req = CreateRequest(_credentials.ShopName, HttpMethod.Get, $"orders/count.json");
- var strResp = await SendRequest(req);
-
- var parsed = JsonConvert.DeserializeObject<CountResponse>(strResp);
-
- return parsed.Count;
- }
-
- public async Task<bool> OrderExists(string orderId)
- {
- var req = CreateRequest(_credentials.ShopName, HttpMethod.Get, $"orders/{orderId}.json?fields=id");
- var strResp = await SendRequest(req);
-
- return strResp?.Contains(orderId, StringComparison.OrdinalIgnoreCase) == true;
- }
- }
-
- public class ShopifyApiClientCredentials
- {
- public string ShopName { get; set; }
- public string ApiKey { get; set; }
- public string ApiPassword { get; set; }
- }
-}
diff --git a/BTCPayServer/Plugins/Shopify/ShopifyApiException.cs b/BTCPayServer/Plugins/Shopify/ShopifyApiException.cs
deleted file mode 100644
index 94cdf54..0000000
--- a/BTCPayServer/Plugins/Shopify/ShopifyApiException.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-using System;
-
-namespace BTCPayServer.Plugins.Shopify
-{
- public class ShopifyApiException : Exception
- {
- public ShopifyApiException(string message) : base(message)
- {
- }
- }
-}
diff --git a/BTCPayServer/Plugins/Shopify/ShopifyExtensions.cs b/BTCPayServer/Plugins/Shopify/ShopifyExtensions.cs
deleted file mode 100644
index 859db06..0000000
--- a/BTCPayServer/Plugins/Shopify/ShopifyExtensions.cs
+++ /dev/null
@@ -1,51 +0,0 @@
-using BTCPayServer.Data;
-using BTCPayServer.Plugins.Shopify.Models;
-using NBitcoin;
-using NBXplorer;
-using Newtonsoft.Json.Linq;
-
-namespace BTCPayServer.Plugins.Shopify
-{
- public static class ShopifyExtensions
- {
- public const string StoreBlobKey = "shopify";
- public static ShopifyApiClientCredentials CreateShopifyApiCredentials(this ShopifySettings shopify)
- {
- return new ShopifyApiClientCredentials
- {
- ShopName = shopify.ShopName,
- ApiKey = shopify.ApiKey,
- ApiPassword = shopify.Password
- };
- }
-
- public static ShopifySettings GetShopifySettings(this StoreBlob storeBlob)
- {
- if (storeBlob.AdditionalData.TryGetValue(StoreBlobKey, out var rawS))
- {
- if (rawS is JObject rawObj)
- {
- return new Serializer(null).ToObject<ShopifySettings>(rawObj);
- }
- else if (rawS.Type == JTokenType.String)
- {
- return new Serializer(null).ToObject<ShopifySettings>(rawS.Value<string>());
- }
-
- }
-
- return null;
- }
- public static void SetShopifySettings(this StoreBlob storeBlob, ShopifySettings settings)
- {
- if (settings is null)
- {
- storeBlob.AdditionalData.Remove(StoreBlobKey);
- }
- else
- {
- storeBlob.AdditionalData.AddOrReplace(StoreBlobKey, new Serializer(null).ToString(settings));
- }
- }
- }
-}
diff --git a/BTCPayServer/Plugins/Shopify/ShopifyPlugin.cs b/BTCPayServer/Plugins/Shopify/ShopifyPlugin.cs
deleted file mode 100644
index 16ffacc..0000000
--- a/BTCPayServer/Plugins/Shopify/ShopifyPlugin.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-using BTCPayServer.Abstractions.Models;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Hosting;
-
-namespace BTCPayServer.Plugins.Shopify
-{
- public class ShopifyPlugin : BaseBTCPayServerPlugin
- {
- public const string Area = "Shopify";
- public override string Identifier => "BTCPayServer.Plugins.Shopify";
- public override string Name => "Shopify";
- public override string Description => "Allows you to integrate BTCPay Server as a payment option in Shopify.";
-
- public override void Execute(IServiceCollection applicationBuilder)
- {
- applicationBuilder.AddSingleton<ShopifyService>();
- applicationBuilder.AddSingleton<IHostedService, ShopifyService>(provider => provider.GetRequiredService<ShopifyService>());
- applicationBuilder.AddUIExtension("header-nav", "/Plugins/Shopify/Views/NavExtension.cshtml");
- base.Execute(applicationBuilder);
- }
- }
-}
diff --git a/BTCPayServer/Plugins/Shopify/ShopifyService.cs b/BTCPayServer/Plugins/Shopify/ShopifyService.cs
deleted file mode 100644
index a8a8253..0000000
--- a/BTCPayServer/Plugins/Shopify/ShopifyService.cs
+++ /dev/null
@@ -1,234 +0,0 @@
-using System;
-using System.Globalization;
-using System.Linq;
-using System.Net.Http;
-using System.Threading;
-using System.Threading.Tasks;
-using BTCPayServer.Client.Models;
-using BTCPayServer.Data;
-using BTCPayServer.Events;
-using BTCPayServer.HostedServices;
-using BTCPayServer.Logging;
-using BTCPayServer.Plugins.Shopify.ApiModels;
-using BTCPayServer.Plugins.Shopify.Models;
-using BTCPayServer.Services.Invoices;
-using BTCPayServer.Services.Stores;
-using Microsoft.Extensions.Logging;
-
-namespace BTCPayServer.Plugins.Shopify
-{
- public class ShopifyService : EventHostedServiceBase
- {
- private readonly StoreRepository _storeRepository;
- private readonly InvoiceRepository _invoiceRepository;
- private readonly IHttpClientFactory _httpClientFactory;
-
- public ShopifyService(EventAggregator eventAggregator,
- StoreRepository storeRepository,
- InvoiceRepository invoiceRepository,
- IHttpClientFactory httpClientFactory,
- Logs logs) : base(eventAggregator, logs)
- {
- _storeRepository = storeRepository;
- _invoiceRepository = invoiceRepository;
- _httpClientFactory = httpClientFactory;
- }
-
- public const string SHOPIFY_ORDER_ID_PREFIX = "shopify-";
-
- protected override void SubscribeToEvents()
- {
- Subscribe<InvoiceEvent>();
- base.SubscribeToEvents();
- }
-
- protected override async Task ProcessEvent(object evt, CancellationToken cancellationToken)
- {
- if (evt is InvoiceEvent invoiceEvent && new[]
- {
- InvoiceEvent.MarkedCompleted,
- InvoiceEvent.MarkedInvalid,
- InvoiceEvent.Expired,
- InvoiceEvent.Confirmed,
- InvoiceEvent.Completed
- }.Contains(invoiceEvent.Name))
- {
- var invoice = invoiceEvent.Invoice;
- var shopifyOrderId = invoice.GetInternalTags(SHOPIFY_ORDER_ID_PREFIX).FirstOrDefault();
- if (shopifyOrderId != null)
- {
- var success = invoice.Status switch
- {
- InvoiceStatus.Settled => true,
- InvoiceStatus.Invalid or InvoiceStatus.Expired => false,
- _ => (bool?)null
- };
-
- if (success.HasValue)
- await RegisterTransaction(invoice, shopifyOrderId, success.Value);
- }
- }
-
- await base.ProcessEvent(evt, cancellationToken);
- }
-
- private async Task RegisterTransaction(InvoiceEntity invoice, string shopifyOrderId, bool success)
- {
- var storeData = await _storeRepository.FindStore(invoice.StoreId);
- var storeBlob = storeData.GetStoreBlob();
-
- // ensure that store in question has shopify integration turned on
- // and that invoice's orderId has shopify specific prefix
- var settings = storeBlob.GetShopifySettings();
- if (settings?.IntegratedAt.HasValue == true)
- {
- var client = CreateShopifyApiClient(settings);
- if (!await client.OrderExists(shopifyOrderId))
- {
- // don't register transactions for orders that don't exist on shopify
- return;
- }
-
- // if we got this far, we likely need to register this invoice's payment on Shopify
- // OrderTransactionRegisterLogic has check if transaction is already registered which is why we're passing invoice.Id
- try
- {
- var resp = await Process(client, shopifyOrderId, invoice.Id, invoice.Currency,
- invoice.Price.ToString(CultureInfo.InvariantCulture), success);
- if (resp != null)
- {
- await _invoiceRepository.AddInvoiceLogs(invoice.Id, resp);
- }
- }
- catch (Exception ex)
- {
- Logs.PayServer.LogError(ex,
- $"Shopify error while trying to register order transaction. " +
- $"Triggered by invoiceId: {invoice.Id}, Shopify orderId: {shopifyOrderId}");
- }
- }
- }
-
-
- private ShopifyApiClient CreateShopifyApiClient(ShopifySettings shopify)
- {
- return new ShopifyApiClient(_httpClientFactory, shopify.CreateShopifyApiCredentials());
- }
-
-
- private static string[] _keywords = new[] {"bitcoin", "btc", "btcpayserver", "btcpay server"};
-
- public async Task<InvoiceLogs> Process(ShopifyApiClient client, string orderId, string invoiceId,
- string currency, string amountCaptured, bool success)
- {
- var result = new InvoiceLogs();
- currency = currency.ToUpperInvariant().Trim();
- var existingShopifyOrderTransactions = (await client.TransactionsList(orderId)).transactions;
-
- //if there isn't a record for btcpay payment gateway, abort
- var baseParentTransaction = existingShopifyOrderTransactions.FirstOrDefault(holder =>
- _keywords.Any(a => holder.gateway.Contains(a, StringComparison.InvariantCultureIgnoreCase)));
- if (baseParentTransaction is null)
- {
- result.Write("Couldn't find the order on Shopify.", InvoiceEventData.EventSeverity.Error);
- return result;
- }
-
- //technically, this exploit should not be possible as we use internal invoice tags to verify that the invoice was created by our controlled, dedicated endpoint.
- if (currency.ToUpperInvariant().Trim() != baseParentTransaction.currency.ToUpperInvariant().Trim())
- {
- // because of parent_id present, currency will always be the one from parent transaction
- // malicious attacker could potentially exploit this by creating invoice
- // in different currency and paying that one, registering order on Shopify as paid
- // so if currency is supplied and is different from parent transaction currency we just won't register
- result.Write("Currency mismatch on Shopify.", InvoiceEventData.EventSeverity.Error);
- return result;
- }
-
- var kind = "capture";
- var parentId = baseParentTransaction.id;
- var status = success ? "success" : "failure";
- //find all existing transactions recorded around this invoice id
- var existingShopifyOrderTransactionsOnSameInvoice =
- existingShopifyOrderTransactions.Where(holder => holder.authorization == invoiceId);
-
- //filter out the successful ones
- var successfulActions =
- existingShopifyOrderTransactionsOnSameInvoice.Where(holder => holder.status == "success").ToArray();
-
- //of the successful ones, get the ones we registered as a valid payment
- var successfulCaptures = successfulActions.Where(holder => holder.kind == "capture").ToArray();
-
- //of the successful ones, get the ones we registered as a voiding of a previous successful payment
- var refunds = successfulActions.Where(holder => holder.kind == "refund").ToArray();
-
- //if we are working with a non-success registration, but see that we have previously registered this invoice as a success, we switch to creating a "void" transaction, which in shopify terms is a refund.
- if (!success && successfulCaptures.Length > 0 && (successfulCaptures.Length - refunds.Length) > 0)
- {
- kind = "void";
- parentId = successfulCaptures.Last().id;
- status = "success";
- result.Write(
- "A transaction was previously recorded against the Shopify order. Creating a void transaction.",
- InvoiceEventData.EventSeverity.Warning);
- }
- else if (!success)
- {
- kind = "void";
- status = "success";
- result.Write("Attempting to void the payment on Shopify order due to failure in payment.",
- InvoiceEventData.EventSeverity.Warning);
- }
- //if we are working with a success registration, but can see that we have already had a successful transaction saved, get outta here
- else if (success && successfulCaptures.Length > 0 && (successfulCaptures.Length - refunds.Length) > 0)
- {
- result.Write("A transaction was previously recorded against the Shopify order. Skipping.",
- InvoiceEventData.EventSeverity.Warning);
- return result;
- }
-
- var createTransaction = new TransactionsCreateReq
- {
- transaction = new TransactionsCreateReq.DataHolder
- {
- parent_id = parentId,
- currency = currency,
- amount = amountCaptured,
- kind = kind,
- gateway = "BTCPayServer",
- source = "external",
- authorization = invoiceId,
- status = status
- }
- };
- var createResp = await client.TransactionCreate(orderId, createTransaction);
-
- if (createResp.transaction is null)
- {
- result.Write("Failed to register the transaction on Shopify.", InvoiceEventData.EventSeverity.Error);
- }
- else
- {
- result.Write(
- $"Successfully registered the transaction on Shopify. tx status:{createResp.transaction.status}, kind: {createResp.transaction.kind}, order id:{createResp.transaction.order_id}",
- InvoiceEventData.EventSeverity.Info);
- }
-
- if (!success)
- {
- try
- {
- await client.CancelOrder(orderId);
- result.Write("Cancelling the Shopify order.", InvoiceEventData.EventSeverity.Warning);
- }
- catch (Exception e)
- {
- result.Write($"Failed to cancel the Shopify order. {e.Message}",
- InvoiceEventData.EventSeverity.Error);
- }
- }
-
- return result;
- }
- }
-}
diff --git a/BTCPayServer/Plugins/Shopify/UIShopifyController.cs b/BTCPayServer/Plugins/Shopify/UIShopifyController.cs
deleted file mode 100644
index d68b4a2..0000000
--- a/BTCPayServer/Plugins/Shopify/UIShopifyController.cs
+++ /dev/null
@@ -1,285 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Net.Http;
-using System.Threading.Tasks;
-using BTCPayServer.Abstractions.Constants;
-using BTCPayServer.Abstractions.Extensions;
-using BTCPayServer.Client;
-using BTCPayServer.Client.Models;
-using BTCPayServer.Configuration;
-using BTCPayServer.Controllers;
-using BTCPayServer.Data;
-using BTCPayServer.Plugins.Shopify.ApiModels;
-using BTCPayServer.Plugins.Shopify.Models;
-using BTCPayServer.Services;
-using BTCPayServer.Services.Invoices;
-using BTCPayServer.Services.Stores;
-using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Cors;
-using Microsoft.AspNetCore.Hosting;
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.AspNetCore.Mvc.Rendering;
-using Microsoft.Extensions.Options;
-using Newtonsoft.Json.Linq;
-using NicolasDorier.RateLimits;
-using StoreData = BTCPayServer.Data.StoreData;
-
-namespace BTCPayServer.Plugins.Shopify
-{
-
- [Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie)]
- [Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
- [Area(ShopifyPlugin.Area)]
- public class UIShopifyController : Controller
- {
- private readonly ShopifyService _shopifyService;
- private readonly BTCPayServerEnvironment _btcPayServerEnvironment;
- private readonly IOptions<BTCPayServerOptions> _btcPayServerOptions;
- private readonly IWebHostEnvironment _webHostEnvironment;
- private readonly StoreRepository _storeRepository;
- private readonly InvoiceRepository _invoiceRepository;
- private readonly UIInvoiceController _invoiceController;
- private readonly IJsonHelper _jsonHelper;
- private readonly IHttpClientFactory _clientFactory;
-
- public UIShopifyController(
- ShopifyService shopifyService,
- BTCPayServerEnvironment btcPayServerEnvironment,
- IOptions<BTCPayServerOptions> btcPayServerOptions,
- IWebHostEnvironment webHostEnvironment,
- StoreRepository storeRepository,
- InvoiceRepository invoiceRepository,
- UIInvoiceController invoiceController,
- IJsonHelper jsonHelper,
- IHttpClientFactory clientFactory)
- {
- _shopifyService = shopifyService;
- _btcPayServerEnvironment = btcPayServerEnvironment;
- _btcPayServerOptions = btcPayServerOptions;
- _webHostEnvironment = webHostEnvironment;
- _storeRepository = storeRepository;
- _invoiceRepository = invoiceRepository;
- _invoiceController = invoiceController;
- _jsonHelper = jsonHelper;
- _clientFactory = clientFactory;
- }
- public StoreData CurrentStore => this.HttpContext.GetStoreData();
- private static string _cachedShopifyJavascript;
-
- private async Task<string> GetJavascript()
- {
- if (!string.IsNullOrEmpty(_cachedShopifyJavascript) && !_btcPayServerEnvironment.IsDeveloping)
- {
- return _cachedShopifyJavascript;
- }
-
- string[] fileList = new[] { "modal/btcpay.js", "shopify/btcpay-shopify.js" };
-
- foreach (var file in fileList)
- {
- await using var stream = _webHostEnvironment.WebRootFileProvider
- .GetFileInfo(file).CreateReadStream();
- using var reader = new StreamReader(stream);
- _cachedShopifyJavascript += Environment.NewLine + await reader.ReadToEndAsync();
- }
-
- return _cachedShopifyJavascript;
- }
-
- [AllowAnonymous]
- [HttpGet("stores/{storeId}/plugins/shopify/shopify.js")]
- public async Task<IActionResult> ShopifyJavascript(string storeId)
- {
- var jsFile =
- $"var BTCPAYSERVER_URL = {_jsonHelper.Serialize(Request.GetAbsoluteRoot())}; var STORE_ID = {_jsonHelper.Serialize(storeId)}; {await GetJavascript()}";
- return Content(jsFile, "text/javascript");
- }
-
- [RateLimitsFilter(ZoneLimits.Shopify, Scope = RateLimitsScope.RemoteAddress)]
- [AllowAnonymous]
- [EnableCors(CorsPolicies.All)]
- [HttpGet("stores/{storeId}/plugins/shopify/{orderId}")]
- public async Task<IActionResult> ShopifyInvoiceEndpoint(
- string storeId, string orderId, decimal amount, bool checkOnly = false)
- {
- var shopifySearchTerm = $"{ShopifyService.SHOPIFY_ORDER_ID_PREFIX}{orderId}";
- var matchedExistingInvoices = await _invoiceRepository.GetInvoices(new InvoiceQuery()
- {
- TextSearch = shopifySearchTerm,
- StoreId = new[] { storeId }
- });
- matchedExistingInvoices = matchedExistingInvoices.Where(entity =>
- entity.GetInternalTags(ShopifyService.SHOPIFY_ORDER_ID_PREFIX)
- .Any(s => s == orderId))
- .ToArray();
-
- var firstInvoiceStillPending =
- matchedExistingInvoices.FirstOrDefault(entity =>
- entity.GetInvoiceState().Status == InvoiceStatus.New);
- if (firstInvoiceStillPending != null)
- {
- return Ok(new
- {
- invoiceId = firstInvoiceStillPending.Id,
- status = firstInvoiceStillPending.Status.ToString().ToLowerInvariant()
- });
- }
-
- var firstInvoiceSettled =
- matchedExistingInvoices.LastOrDefault(entity =>
- new[] { InvoiceStatus.Processing, InvoiceStatus.Settled }
- .Contains(
- entity.GetInvoiceState().Status));
-
- var store = await _storeRepository.FindStore(storeId);
- var shopify = store?.GetStoreBlob()?.GetShopifySettings();
- ShopifyApiClient client = null;
- ShopifyOrder order = null;
- if (shopify?.IntegratedAt.HasValue is true)
- {
- client = new ShopifyApiClient(_clientFactory, shopify.CreateShopifyApiCredentials());
- order = await client.GetOrder(orderId);
- if (order?.Id is null)
- {
- return NotFound();
- }
- }
-
- if (firstInvoiceSettled != null)
- {
- //if BTCPay was shut down before the tx managed to get registered on shopify, this will fix it on the next UI load in shopify
- if (client != null && order?.FinancialStatus == "pending" &&
- firstInvoiceSettled.Status != InvoiceStatus.Processing)
- {
- await _shopifyService.Process(client, orderId, firstInvoiceSettled.Id,
- firstInvoiceSettled.Currency,
- firstInvoiceSettled.Price.ToString(CultureInfo.InvariantCulture), true);
- order = await client.GetOrder(orderId);
- }
-
- return Ok(new
- {
- invoiceId = firstInvoiceSettled.Id,
- status = firstInvoiceSettled.Status.ToString().ToLowerInvariant()
- });
- }
-
- if (checkOnly)
- {
- return Ok();
- }
-
- if (shopify?.IntegratedAt.HasValue is true)
- {
- if (order?.Id is null ||
- !new[] { "pending", "partially_paid" }.Contains(order.FinancialStatus))
- {
- return NotFound();
- }
-
- //we create the invoice at due amount provided from order page or full amount if due amount is bigger than order amount
- var invoice = await _invoiceController.CreateInvoiceCoreRaw(
- new CreateInvoiceRequest()
- {
- Amount = amount < order.TotalOutstanding ? amount : order.TotalOutstanding,
- Currency = order.PresentmentCurrency,
- Metadata = new JObject
- {
- ["orderId"] = order.OrderNumber,
- ["shopifyOrderId"] = order.Id,
- ["shopifyOrderNumber"] = order.OrderNumber
- },
- AdditionalSearchTerms = new[]
- {
- order.OrderNumber.ToString(CultureInfo.InvariantCulture),
- order.Id.ToString(CultureInfo.InvariantCulture),
- shopifySearchTerm
- }
- }, store,
- Request.GetAbsoluteRoot(), new List<string>() { shopifySearchTerm });
-
- return Ok(new { invoiceId = invoice.Id, status = invoice.Status.ToString().ToLowerInvariant() });
- }
-
- return NotFound();
- }
-
- [HttpGet]
- [Route("stores/{storeId}/plugins/shopify")]
- public IActionResult EditShopify()
- {
- var blob = CurrentStore.GetStoreBlob();
-
- return View(blob.GetShopifySettings());
- }
-
-
- [HttpPost("stores/{storeId}/plugins/shopify")]
- public async Task<IActionResult> EditShopify(string storeId,
- ShopifySettings vm, string command = "")
- {
- switch (command)
- {
- case "ShopifySaveCredentials":
- {
- var shopify = vm;
- var validCreds = shopify != null && shopify?.CredentialsPopulated() == true;
- if (!validCreds)
- {
- TempData[WellKnownTempData.ErrorMessage] = "Please provide valid Shopify credentials";
- return View(vm);
- }
- var apiClient = new ShopifyApiClient(_clientFactory, shopify.CreateShopifyApiCredentials());
- try
- {
- await apiClient.OrdersCount();
- }
- catch (ShopifyApiException err)
- {
- TempData[WellKnownTempData.ErrorMessage] = err.Message;
- return View(vm);
- }
-
- var scopesGranted = await apiClient.CheckScopes();
- if (!scopesGranted.Contains("read_orders") || !scopesGranted.Contains("write_orders"))
- {
- TempData[WellKnownTempData.ErrorMessage] =
- "Please grant the private app permissions for read_orders, write_orders";
- return View(vm);
- }
-
- // everything ready, proceed with saving Shopify integration credentials
- shopify.IntegratedAt = DateTimeOffset.Now;
-
- var blob = CurrentStore.GetStoreBlob();
- blob.SetShopifySettings(shopify);
- if (CurrentStore.SetStoreBlob(blob))
- {
- await _storeRepository.UpdateStore(CurrentStore);
- }
-
- TempData[WellKnownTempData.SuccessMessage] = "Shopify plugin successfully updated";
- break;
- }
- case "ShopifyClearCredentials":
- {
- var blob = CurrentStore.GetStoreBlob();
- blob.SetShopifySettings(null);
- if (CurrentStore.SetStoreBlob(blob))
- {
- await _storeRepository.UpdateStore(CurrentStore);
- }
-
- TempData[WellKnownTempData.SuccessMessage] = "Shopify plugin credentials cleared";
- break;
- }
- }
-
- return RedirectToAction(nameof(EditShopify), new { storeId = CurrentStore.Id });
- }
- }
-
-}
diff --git a/BTCPayServer/Plugins/Shopify/Views/EditShopify.cshtml b/BTCPayServer/Plugins/Shopify/Views/EditShopify.cshtml
deleted file mode 100644
index 70adb5a..0000000
--- a/BTCPayServer/Plugins/Shopify/Views/EditShopify.cshtml
+++ /dev/null
@@ -1,101 +0,0 @@
-@model BTCPayServer.Plugins.Shopify.Models.ShopifySettings
-@{
- Layout = "_Layout";
- ViewData.SetLayoutModel(new LayoutModel("shopify", StringLocalizer["Shopify"]).SetCategory(WellKnownCategories.Store));
- var shopifyCredsSet = Model?.IntegratedAt.HasValue is true;
- var shopifyUrl = Model?.ShopifyUrl;
-}
-
-<div class="alert alert-warning alert-dismissible mb-4" role="alert">
- <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="@StringLocalizer["Close"]">
- <vc:icon symbol="close" />
- </button>
- <h5 class="alert-heading" text-translate="true">Important notice</h5>
- <p class="my-2 mb-5">This Shopify integration has been discontinued by Shopify and will no longer be supported after <strong>August 31, 2025</strong>. If you completed your BTCPay Server-Shopify setup before <strong>December 31, 2024</strong>, you may continue using it until August 31, 2025. However, we recommend transitioning to Shopify V2 for continued functionality. All new users have to use Shopify V2. <a href="https://docs.btcpayserver.org/ShopifyV2/" rel="noreferrer noopener" class="alert-link" text-translate="true">Refer to this guide to get started with Shopify V2</a></p>
-</div>
-
-
-<div class="sticky-header">
- <h2 class="my-1">
- @ViewData["Title"]
- <small>
- <a href="https://docs.btcpayserver.org/Shopify" target="_blank" rel="noreferrer noopener" title="@StringLocalizer["More information..."]">
- <vc:icon symbol="info" />
- </a>
- </small>
- </h2>
-</div>
-
-<partial name="_StatusMessage" />
-
-<div class="row">
- <div class="col-xxl-constrain col-xl-8">
- <p text-translate="true">Connect BTCPay Server to your Shopify checkout experience to accept Bitcoin.</p>
-
- @if (!ViewContext.ModelState.IsValid)
- {
- <div asp-validation-summary="All"></div>
- }
-
- <form method="post" id="shopifyForm">
- <div class="form-group">
- <label asp-for="ShopName" class="form-label"></label>
- <div class="input-group">
- @if (!Model?.ShopName?.Contains(".") is true)
- {
- <span class="input-group-text">https://</span>
- }
- <input asp-for="ShopName" class="form-control" readonly="@shopifyCredsSet"/>
-
- @if (!shopifyCredsSet || !Model?.ShopName?.Contains(".") is true)
- {
- <span class="input-group-text">.myshopify.com</span>
- }
- </div>
- <span asp-validation-for="ShopName" class="text-danger"></span>
- </div>
-
- <div class="form-group">
- <label asp-for="ApiKey" class="form-label"></label>
- <input asp-for="ApiKey" class="form-control" readonly="@shopifyCredsSet"/>
- <span asp-validation-for="ApiKey" class="text-danger"></span>
- </div>
-
- <div class="form-group">
- <label asp-for="Password" class="form-label"></label>
- <input asp-for="Password" class="form-control" type="password" value="@Model?.Password" readonly="@shopifyCredsSet"/>
- <span asp-validation-for="Password" class="text-danger"></span>
- </div>
-
- @if (shopifyCredsSet)
- {
- <div class="alert alert-warning">
- <h5 class="alert-heading" text-translate="true">In Shopify, please do the following …</h5>
- <ul>
- <li>
- Add a payment method at <a href="@shopifyUrl/admin/settings/payments" class="alert-link fw-bold" target="_blank" rel="noreferrer noopener">Settings > Payments > Manual Payment Methods</a> with the name <strong>Bitcoin with BTCPay Server</strong>
- </li>
- <li>
- Paste this script at <a href="@shopifyUrl/admin/settings/checkout#PolarisTextField1" class="fw-bold alert-link" target="_blank" rel="noreferrer noopener">Settings > Checkout > Order Processing > Additional Scripts</a>:
- <pre class="mt-2 mb-0 text-wrap">@($"<script src='{Url.Action("ShopifyJavascript", "UIShopify", new { storeId = Context.GetRouteValue("storeId") }, Context.Request.Scheme)}'></script>")</pre>
- </li>
- </ul>
- </div>
- <p class="alert alert-success">
- Orders on <b>@Model.ShopName</b>.myshopify.com will be marked as paid on successful invoice payment.
- Started: @Model.IntegratedAt.Value.ToBrowserDate()
- </p>
-
- <button name="command" type="submit" class="btn btn-danger mt-3" value="ShopifyClearCredentials" text-translate="true">
- Stop Shopify calls and clear credentials
- </button>
- }
- else
- {
- <button name="command" type="submit" class="btn btn-primary mt-3" value="ShopifySaveCredentials" text-translate="true">
- Save
- </button>
- }
- </form>
- </div>
-</div>
diff --git a/BTCPayServer/Plugins/Shopify/Views/NavExtension.cshtml b/BTCPayServer/Plugins/Shopify/Views/NavExtension.cshtml
deleted file mode 100644
index 2889a46..0000000
--- a/BTCPayServer/Plugins/Shopify/Views/NavExtension.cshtml
+++ /dev/null
@@ -1,16 +0,0 @@
-@using BTCPayServer.Client
-@using BTCPayServer.Plugins.Shopify
-@model BTCPayServer.Components.MainNav.MainNavViewModel
-@{
- var store = Model.Store;
-}
-
-@if (store != null)
-{
- <li class="nav-item" permission="@Policies.CanModifyStoreSettings">
- <a layout-menu-item="Shopify" asp-area="@ShopifyPlugin.Area" asp-controller="UIShopify" asp-action="EditShopify" asp-route-storeId="@store.Id">
- <vc:icon symbol="logo-shopify" />
- <span text-translate="true">Shopify</span>
- </a>
- </li>
-}
diff --git a/BTCPayServer/Plugins/Translations/Translations.Default.cs b/BTCPayServer/Plugins/Translations/Translations.Default.cs
index 5873547..f57fd85 100644
--- a/BTCPayServer/Plugins/Translations/Translations.Default.cs
+++ b/BTCPayServer/Plugins/Translations/Translations.Default.cs
@@ -354,7 +354,6 @@ namespace BTCPayServer.Plugins.Translations
"Confirmations": "",
"confirmed": "",
"Connect an existing wallet": "",
- "Connect BTCPay Server to your Shopify checkout experience to accept Bitcoin.": "",
"Connect hardware wallet": "",
"Connect to a Lightning node": "",
"Connect your hardware wallet": "",
@@ -811,7 +810,6 @@ namespace BTCPayServer.Plugins.Translations
"Impossible to fetch rate: {0}": "",
"In order to securely connect to your hardware wallet you must first download, install, and run the BTCPay Server Vault.": "",
"In order to upload, a {0} must be configured.": "",
- "In Shopify, please do the following …": "",
"In use": "",
"In-store": "",
"Inactive": "",
@@ -1321,7 +1319,6 @@ namespace BTCPayServer.Plugins.Translations
"Redirect invoice to redirect url automatically after paid": "",
"Redirect URL": "",
"Redirects": "",
- "Refer to this guide to get started with Shopify V2": "",
"Reference Id": "",
"Refund": "",
"Refund {0}": "",
@@ -1517,8 +1514,6 @@ namespace BTCPayServer.Plugins.Translations
"Setup {0} Wallet": "",
"Setup Boltcard": "",
"Setup new wallet": "",
- "Shop Name": "",
- "Shopify": "",
"Show \"Pay in wallet\" button": "",
"Show a timer … minutes before invoice expiration": "",
"Show all": "",
@@ -1583,7 +1578,6 @@ namespace BTCPayServer.Plugins.Translations
"Status": "",
"Step": "",
"Still due": "",
- "Stop Shopify calls and clear credentials": "",
"Storage Provider": "",
"Storage settings updated successfully": "",
"Store": "",
diff --git a/BTCPayServer/Security/GreenField/APIKeyRepository.cs b/BTCPayServer/Security/GreenField/APIKeyRepository.cs
index 1d564e9..c02dc31 100644
--- a/BTCPayServer/Security/GreenField/APIKeyRepository.cs
+++ b/BTCPayServer/Security/GreenField/APIKeyRepository.cs
@@ -4,7 +4,6 @@ using System.Linq;
using System.Threading.Tasks;
using BTCPayServer.Client;
using BTCPayServer.Data;
-using BTCPayServer.Plugins.Shopify.ApiModels;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Internal;
using Npgsql;
diff --git a/BTCPayServer/ZoneLimits.cs b/BTCPayServer/ZoneLimits.cs
index 42e1faf..691eb80 100644
--- a/BTCPayServer/ZoneLimits.cs
+++ b/BTCPayServer/ZoneLimits.cs
@@ -5,7 +5,6 @@ namespace BTCPayServer
public const string Login = "btcpaylogin";
public const string Register = "btcpayregister";
public const string PayJoin = "PayJoin";
- public const string Shopify = nameof(Shopify);
public const string ForgotPassword = "forgotpassword";
public const string PublicInvoices = "publicinvoices";
}
diff --git a/BTCPayServer/wwwroot/img/icon-sprite.svg b/BTCPayServer/wwwroot/img/icon-sprite.svg
index 18048e6..6150b8d 100644
--- a/BTCPayServer/wwwroot/img/icon-sprite.svg
+++ b/BTCPayServer/wwwroot/img/icon-sprite.svg
@@ -54,7 +54,6 @@
<symbol id="logo-btcpay" viewBox="0 0 24 24" fill="none"><g fill="currentColor" transform="matrix(0.27159594,0,0,0.27159594,5.6093989,0.60189406)"><path d="M 5.206,83.433 A 4.86,4.86 0 0 1 0.347,78.572 V 5.431 a 4.86,4.86 0 1 1 9.719,0 v 73.141 a 4.861,4.861 0 0 1 -4.86,4.861"/><path d="M 5.209,83.433 A 4.862,4.862 0 0 1 3.123,74.18 L 32.43,60.274 2.323,38.093 a 4.861,4.861 0 0 1 5.766,-7.826 l 36.647,26.999 a 4.864,4.864 0 0 1 -0.799,8.306 L 7.289,82.964 a 4.866,4.866 0 0 1 -2.08,0.469"/><path d="M 5.211,54.684 A 4.86,4.86 0 0 1 2.324,45.91 L 32.43,23.73 3.123,9.821 A 4.861,4.861 0 0 1 7.289,1.037 l 36.648,17.394 a 4.86,4.86 0 0 1 0.799,8.305 l -36.647,27 a 4.844,4.844 0 0 1 -2.878,0.948"/><path d="M 10.066,31.725 V 52.278 L 24.01,42.006 Z"/><path d="M 10.066,5.431 A 4.861,4.861 0 0 0 5.206,0.57 4.86,4.86 0 0 0 0.347,5.431 v 61.165 h 9.72 V 5.431 Z"/></g></symbol>
<symbol id="logo-lightningterminal" viewBox="0 0 28 55" fill="none"><g fill="currentColor"><path d="m27.25 30.5-15.9 23.2a.84.84 0 1 1-1.38-.96l15.9-23.19a.84.84 0 1 1 1.38.96zm-2.09-4.13L9.63 49.08a.84.84 0 0 1-1.39-.95l15.54-22.71a.84.84 0 0 1 1.38.95zm-4.72-24.8L2.43 27.9h16.9l-1.14 1.68H.36a.84.84 0 0 1-.22-1.15L19 .62A.84.84 0 0 1 20.16.4c.4.26.52.78.28 1.19z"/><path d="M22.12 6.62 10.24 23.99H22l-1.15 1.68H7.05l1.14-1.68 12.53-18.3a.84.84 0 0 1 1.39.93z"/></g></symbol>
<symbol id="logo-rtl" viewBox="0 0 610 524" fill="none"><path d="M418.62 107.6c1.95 4.6 2.73 8.24 2.63 10.24-7.56-4.56-18.93-8.43-25.14-9.54-4.03-.72 9.18-3.93 22.51-.7m10.99 169.33a476.42 476.42 0 0 1 14.43-2.8 447.25 447.25 0 0 0-5.98-3.71c-25.45-15.5-52.58-28.64-79.4-41.37-16.91-8.03-34.15-16.02-51.7-23.22 15.31-20.19 34.91-37.32 55.86-52 .02 0 .1-.07.23-.16 4.59 6.26 10.38 10.51 13.29 12.11 5.68 3.13 12.84 6.06 19.41 6.95a110.45 110.45 0 0 0 35.44-.55c1.06-.18 2.14-.3 3.2-.49a105.01 105.01 0 0 1 16.87-1.54c5.88-.1 8.79 1.14 9.48 1.32 2.1.54 3.89 1.44 4.88 2.64a39.71 39.71 0 0 0 3.7 4.13c3.96 3.93 7.89 6.11 13.58 6.74 7.34.8 12.53-2.07 16.2-6.83 1.74-2.25.9-5.78.71-6.47-.96-3.55-3.18-8.7-4.8-13.5a27.3 27.3 0 0 0-4.86-7.64l-2.42-2.56c-3.78-4-7.59-7.96-11.36-11.95-15.06-15.92-31.38-30.58-48.21-44.59l-5.24-4.36-3.82-3.16a1032.74 1032.74 0 0 0-6.06-4.96l-1.63-1.31c-.86-.7-2.81-2.34-4.46-3.66 5.46-5.44 9.86-8.83 17.68-13.95 1.12-.74 7.16-3.96 6.95-4.78-.13-.49-10.05-.38-21.4 1.09-4 .51-26.38 3.81-41.74 7.16-16.35 3.56-33.62 8.55-49.53 13.55-45.94 14.47-89.81 34.07-129.99 60.7-23.09 15.32-44.7 32.21-65.56 50.42a1066.8 1066.8 0 0 0-28.81 25.95 696.64 696.64 0 0 0-4.41 4.19c2 .37 4.01.76 6.02 1.18 16.6 3.4 33.04 7.97 49.02 12.53 19.53 5.59 39.3 11.6 58.41 19a583.18 583.18 0 0 0-31.63 32.21 765.5 765.5 0 0 0-53.37 66.63c-21.6 30.15-40.02 62.67-56.09 96.17a765.04 765.04 0 0 0-4.5 9.54c2.97-2.38 5.96-4.74 8.97-7.08 26.01-20.17 53.44-38.82 80.82-56.84 38.53-25.35 79.88-46.3 122.78-63.16 51.73-20.32 104.56-40.03 159.04-51.57" fill="currentColor"/></symbol>
-<symbol id="logo-shopify" viewBox="0 0 32 32" fill="none"><path transform="scale(.7) translate(5, 5)" d="m20.45 31.97 9.62-2.08-3.5-23.64c-.03-.16-.15-.26-.28-.26l-2.57-.18s-1.7-1.7-1.92-1.88a.41.41 0 0 0-.16-.1l-1.22 28.14zm-4.83-16.9s-1.09-.56-2.37-.56c-1.93 0-2 1.2-2 1.52 0 1.64 4.31 2.29 4.31 6.17 0 3.06-1.92 5.01-4.54 5.01-3.14 0-4.72-1.95-4.72-1.95l.86-2.78s1.66 1.42 3.04 1.42c.9 0 1.3-.72 1.3-1.24 0-2.16-3.54-2.26-3.54-5.81-.04-2.98 2.1-5.9 6.44-5.9 1.68 0 2.5.49 2.5.49l-1.26 3.62zM14.9 1.1c.17 0 .36.06.53.19-1.31.62-2.75 2.18-3.34 5.32-.88.28-1.73.54-2.52.77.69-2.38 2.36-6.26 5.33-6.26zm1.64 3.94v.18l-3.2.98c.63-2.37 1.79-3.53 2.79-3.96.26.67.41 1.57.41 2.8zm.72-2.98c.92.1 1.52 1.15 1.9 2.34-.46.15-.98.3-1.54.49v-.34c0-1-.13-1.82-.36-2.5zm3.99 1.72-.1.03c-.03 0-.39.1-.96.28-.56-1.65-1.56-3.16-3.34-3.16h-.16C16.2.28 15.56 0 15.02 0 10.88 0 8.9 5.17 8.28 7.8c-1.6.48-2.75.84-2.88.9-.9.28-.93.3-1.03 1.15-.1.62-2.44 18.75-2.44 18.75L20.01 32z" fill="currentColor"/></symbol>
<symbol id="logo-thunderhub" viewBox="0 0 24 24" fill="none"><g stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="4" width="16" height="16" rx="2" ry="2"/><rect x="9" y="9" width="6" height="6"/><line x1="9" y1="1" x2="9" y2="4"/><line x1="15" y1="1" x2="15" y2="4"/><line x1="9" y1="20" x2="9" y2="23"/><line x1="15" y1="20" x2="15" y2="23"/><line x1="20" y1="9" x2="23" y2="9"/><line x1="20" y1="14" x2="23" y2="14"/><line x1="1" y1="9" x2="4" y2="9"/><line x1="1" y1="14" x2="4" y2="14"/></g></symbol>
<symbol id="logo-tor" viewBox="0 0 61 91" fill="none"><g fill="currentColor"><path d="m34.9 6.8-2.4 9.6C35.9 9.6 41.4 4.5 47.7 0c-4.6 5.3-8.8 10.6-11.3 16C40.7 9.9 46.5 6.6 53 4.3 44.3 12 37.4 20.4 32.2 28.7L28 26.9c.7-6.7 3.2-13.5 6.9-20.1z"/><path d="m31.7 28.3 2.9 1.5c-.3 1.9.1 6.1 2 7.1 8.4 5.2 16.2 10.8 19.3 16.5 11 19.9-7.7 38.4-24 36.6 8.8-6.5 11.4-19.9 8.1-34.6-1.3-5.7-3.4-10.9-7.1-16.8-1.6-2.7-1-6.3-1.2-10.3zM28.5 37.8c-.6 3.1-1.3 8.7-4 10.8-1.1.8-2.3 1.6-3.5 2.4-4.9 3.3-9.7 6.4-11.9 14.3-.5 1.7-.1 3.5.3 5.2 1.2 4.9 4.6 10.1 7.3 13.2 0 .1.5.5.5.6 2.2 2.6 2.9 3.4 11.3 5.3l-.2.9c-5.1-1.3-9.2-2.6-11.9-5.6 0-.1-.5-.5-.5-.5-2.8-3.2-6.3-8.6-7.5-13.7-.5-2-.9-3.6-.3-5.7 2.3-8.2 7.3-11.5 12.3-14.9 1.1-.7 2.5-1.4 3.6-2.3 2.3-1.5 3.4-6.2 4.5-10z"/><path d="M30.7 50.8c.1 3.5-.3 5.3.6 7.8.5 1.5 2.4 3.5 2.9 5.5.7 2.6 1.5 5.5 1.5 7.3 0 2-.1 5.8-1 9.8-.7 3.3-2.2 6.2-4.8 7.8-2.7-.5-5.8-1.5-7.6-3.1-3.6-3.1-6.7-8.3-7.1-12.8-.3-3.7 3.1-9.2 7.9-11.9 4-2.4 5-5 5.9-9.4-1.2 3.8-2.4 6.9-6.3 9-5.7 3-8.6 7.9-8.3 12.7.4 6.1 2.8 10.2 7.6 13.5 2 1.4 5.8 2.9 8.2 3.3V90c1.8-.3 4.1-3.3 5.3-7.2 1-3.6 1.4-8.1 1.3-11-.1-1.7-.8-5.3-2.2-8.6-.7-1.8-1.9-3.6-2.6-4.9-.9-1.5-.9-4.3-1.3-7.5z"/><path d="M30.1 64.5c.1 2.4 1 5.4 1.4 8.5.3 2.3.2 4.6.1 6.6-.1 2.3-.8 6.5-1.9 8.6-1-.5-1.4-1-2.1-1.8-.8-1.1-1.4-2.3-1.9-3.6-.4-1-.9-2.2-1.1-3.5-.3-2-.2-5.2 2.1-8.4 1.8-2.6 2.2-2.8 2.8-5.7-.8 2.6-1.4 2.9-3.3 5.1-2.1 2.4-2.4 6-2.4 8.9 0 1.2.5 2.6 1 3.8.5 1.3 1 2.7 1.7 3.7 1.1 1.6 2.5 2.6 3.2 2.7v-.1c1.3-1.5 2.1-2.9 2.4-4.4.3-1.8.4-3.5.6-5.6.2-1.8.1-4.1-.4-6.5-.6-3-1.7-6.1-2.2-8.3z"/><path d="M30.5 35c.1 3.5.3 10 1.3 12.6.3.9 2.8 4.7 4.5 9.4 1.2 3.2 1.5 6.2 1.7 7.1.8 3.8-.2 10.3-1.5 16.4-.7 3.3-3 7.4-5.6 9l-.5.9c1.5-.1 5.1-3.6 6.4-8.1 2.2-7.5 3-11 2-19.4-.1-.8-.5-3.6-1.8-6.5-1.9-4.5-4.6-8.8-4.9-9.7-.7-1.4-1.5-7.6-1.6-11.7z"/><path d="M31.7 28.6c-.2 3.6-.2 6.4.4 9.1.7 2.9 4.5 7.1 6.1 11.9 3 9.2 2.2 21.2.1 30.5-.8 3.3-4.6 8.1-8.5 9.6l2.8.7c1.5-.1 5.5-3.8 7.1-8 2.5-6.7 3-14.6 2-23-.1-.8-1.4-8-2.7-11-1.8-4.5-4.7-7.7-5.7-10.5-.8-2.1-1.1-7.7-.6-8.8l-1-.5z"/><path d="M51.7 46.3c-2.9-2.6-6.5-4.8-10.3-6.9-1.7-.9-6.9-5-5.1-10.8l-13.1-5.4-.9.7c4.4 7.9 2.1 12.1-.1 13.5-4.4 3-10.8 6.8-13.9 10.1C2.2 53.8.4 59.8 1 67.6c.6 10.1 7.9 18.5 17.8 21.8 4.3 1.4 8.3 1.6 12.7 1.6 7.1 0 14.5-1.9 19.8-6.3 5.7-4.7 9-11.8 9-19.1 0-7.3-3.1-14.3-8.6-19.3zm-1.9 36.9c-4.9 4-13.7 6.8-18.4 6.6-5.2-.3-10.3-1.1-14.8-3.3C8.7 82.7 3.5 74.4 3.1 67.7 2.4 54 9 50.1 15.1 45.1c3.4-2.8 8.2-4.2 10.9-9.2.5-1.1.8-3.5.2-6-.3-.9-1.5-3.9-2-4.6l9.8 4.3c-1.2 4.5 2.5 9.2 5.5 10.9 3 1.7 7.7 4.9 10.6 7.5 5.1 4.5 7.7 10.9 7.7 17.6 0 6.7-2.8 13.3-8 17.6z"/></g></symbol>
<symbol id="minus" viewBox="0 0 16 16" fill="none"><path d="M14 7H2V9H14V7Z" fill="currentColor"/></symbol>
@@ -124,4 +123,4 @@
<symbol id="wallet-watchonly" viewBox="0 0 32 32" fill="none"><path d="M26.5362 7.08746H25.9614V3.25512C25.9614 2.10542 25.3865 1.14734 24.6201 0.572488C23.8536 -0.00236247 22.7039 -0.193979 21.7458 -0.00236246L4.11707 5.36291C2.00929 5.93776 0.667969 7.85392 0.667969 9.96171V12.0695V12.836V27.3988C0.667969 30.0815 2.77575 32.1893 5.45839 32.1893H26.5362C29.2189 32.1893 31.3267 30.0815 31.3267 27.3988V12.0695C31.3267 9.38686 29.2189 7.08746 26.5362 7.08746ZM4.69192 7.08746L22.129 1.91381C22.5123 1.72219 23.0871 1.91381 23.4704 2.10542C23.8536 2.29704 24.0452 2.87189 24.0452 3.25512V7.08746H5.45839C4.88354 7.08746 4.5003 7.27908 3.92545 7.47069C4.11707 7.27908 4.5003 7.08746 4.69192 7.08746ZM29.4105 27.2072C29.4105 28.7402 28.0692 30.0815 26.5362 30.0815H5.45839C3.92545 30.0815 2.58414 28.7402 2.58414 27.2072V12.836V11.8779C2.58414 10.3449 3.92545 9.00362 5.45839 9.00362H26.5362C28.0692 9.00362 29.4105 10.3449 29.4105 11.8779V27.2072Z" fill="currentColor"/><path d="M25.9591 21.6487C27.0174 21.6487 27.8753 20.7908 27.8753 19.7326C27.8753 18.6743 27.0174 17.8164 25.9591 17.8164C24.9009 17.8164 24.043 18.6743 24.043 19.7326C24.043 20.7908 24.9009 21.6487 25.9591 21.6487Z" fill="currentColor"/></symbol>
<symbol id="wallet-xpub" viewBox="0 0 24 24" fill="none"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.5 9.5C10.5003 8.65387 10.769 7.82965 11.2673 7.14583C11.7657 6.46202 12.468 5.95384 13.2733 5.69436C14.0787 5.43488 14.9456 5.43748 15.7494 5.70176C16.5532 5.96605 17.2525 6.47842 17.7467 7.1652C18.2409 7.85198 18.5047 8.6778 18.4999 9.52392C18.4952 10.37 18.2223 11.1929 17.7205 11.8741C17.2186 12.5553 16.5137 13.0599 15.707 13.3152C14.9003 13.5705 14.0334 13.5634 13.231 13.295C13.0989 13.2507 12.9571 13.2441 12.8214 13.2759C12.6858 13.3077 12.5617 13.3766 12.463 13.475L11.44 14.5H10.25C10.0511 14.5 9.86032 14.579 9.71967 14.7197C9.57902 14.8603 9.5 15.0511 9.5 15.25V16.44L9.44 16.5H8.25C8.05109 16.5 7.86032 16.579 7.71967 16.7197C7.57902 16.8603 7.5 17.0511 7.5 17.25V18.44L7.44 18.5H5.75C5.6837 18.5 5.62011 18.4737 5.57322 18.4268C5.52634 18.3799 5.5 18.3163 5.5 18.25V16.56L10.524 11.537C10.6226 11.4384 10.6917 11.3144 10.7237 11.1787C10.7556 11.0431 10.7492 10.9012 10.705 10.769C10.5686 10.3598 10.4994 9.93129 10.5 9.5ZM14.5 4C13.6681 3.99986 12.847 4.18843 12.0985 4.55153C11.35 4.91463 10.6937 5.44279 10.1788 6.09625C9.66398 6.74971 9.30409 7.51145 9.12623 8.32412C8.94837 9.13679 8.95718 9.97923 9.152 10.788L4.22 15.72C4.07931 15.8605 4.00018 16.0512 4 16.25V18.25C4 19.216 4.784 20 5.75 20H7.75C7.94885 19.9998 8.13948 19.9207 8.28 19.78L8.78 19.28C8.92069 19.1395 8.99983 18.9488 9 18.75V18H9.75C9.94885 17.9998 10.1395 17.9207 10.28 17.78L10.78 17.28C10.9207 17.1395 10.9998 16.9488 11 16.75V16H11.75C11.9488 15.9998 12.1395 15.9207 12.28 15.78L13.212 14.848C13.9672 15.0293 14.7523 15.0487 15.5155 14.9047C16.2787 14.7608 17.0028 14.4568 17.64 14.0128C18.2773 13.5689 18.8133 12.995 19.2128 12.3289C19.6123 11.6628 19.8662 10.9197 19.9577 10.1485C20.0493 9.37721 19.9765 8.59531 19.7441 7.85422C19.5117 7.11314 19.125 6.42968 18.6094 5.84883C18.0938 5.26798 17.461 4.80292 16.7527 4.48426C16.0444 4.16559 15.2767 4.00055 14.5 4ZM15 10C15.2652 10 15.5196 9.89464 15.7071 9.70711C15.8946 9.51957 16 9.26522 16 9C16 8.73478 15.8946 8.48043 15.7071 8.29289C15.5196 8.10536 15.2652 8 15 8C14.7348 8 14.4804 8.10536 14.2929 8.29289C14.1054 8.48043 14 8.73478 14 9C14 9.26522 14.1054 9.51957 14.2929 9.70711C14.4804 9.89464 14.7348 10 15 10Z" fill="currentColor"/></symbol>
<symbol id="warning" viewBox="0 0 24 24" fill="none"><path fill-rule="evenodd" clip-rule="evenodd" d="M12.2528 5.32603C12.2282 5.28043 12.1917 5.24233 12.1472 5.21577C12.1027 5.18922 12.0518 5.1752 12 5.1752C11.9482 5.1752 11.8973 5.18922 11.8528 5.21577C11.8083 5.24233 11.7718 5.28043 11.7472 5.32603L4.75786 18.4014C4.73445 18.4451 4.72276 18.4942 4.72393 18.5438C4.7251 18.5934 4.7391 18.6419 4.76455 18.6844C4.79001 18.727 4.82606 18.7623 4.86919 18.7868C4.91233 18.8113 4.96107 18.8242 5.01068 18.8243H18.9893C19.0389 18.8242 19.0877 18.8113 19.1308 18.7868C19.1739 18.7623 19.21 18.727 19.2354 18.6844C19.2609 18.6419 19.2749 18.5934 19.2761 18.5438C19.2772 18.4942 19.2656 18.4451 19.2421 18.4014L12.2528 5.32603ZM10.2268 4.51356C10.9841 3.09547 13.0159 3.09547 13.7732 4.51356L20.7625 17.5889C20.9263 17.8953 21.0079 18.2389 20.9994 18.5862C20.9909 18.9335 20.8926 19.2727 20.714 19.5707C20.5354 19.8687 20.2827 20.1154 19.9804 20.2867C19.6782 20.458 19.3367 20.548 18.9893 20.5481H5.01068C4.66327 20.548 4.3218 20.458 4.01955 20.2867C3.7173 20.1154 3.46457 19.8687 3.286 19.5707C3.10743 19.2727 3.0091 18.9335 3.0006 18.5862C2.9921 18.2389 3.07372 17.8953 3.2375 17.5889L10.2268 4.51356ZM13.1492 15.9514C13.1492 16.2561 13.0281 16.5484 12.8126 16.764C12.5971 16.9795 12.3048 17.1005 12 17.1005C11.6952 17.1005 11.4029 16.9795 11.1874 16.764C10.9719 16.5484 10.8508 16.2561 10.8508 15.9514C10.8508 15.6466 10.9719 15.3543 11.1874 15.1388C11.4029 14.9233 11.6952 14.8022 12 14.8022C12.3048 14.8022 12.5971 14.9233 12.8126 15.1388C13.0281 15.3543 13.1492 15.6466 13.1492 15.9514ZM12.8619 9.91816C12.8619 9.68957 12.7711 9.47035 12.6094 9.30871C12.4478 9.14708 12.2286 9.05627 12 9.05627C11.7714 9.05627 11.5522 9.14708 11.3906 9.30871C11.2289 9.47035 11.1381 9.68957 11.1381 9.91816V12.7911C11.1381 13.0197 11.2289 13.2389 11.3906 13.4006C11.5522 13.5622 11.7714 13.653 12 13.653C12.2286 13.653 12.4478 13.5622 12.6094 13.4006C12.7711 13.2389 12.8619 13.0197 12.8619 12.7911V9.91816Z" fill="currentColor"/></symbol>
-</svg>
\ No newline at end of file
+</svg>
diff --git a/BTCPayServer/wwwroot/shopify/btcpay-shopify.js b/BTCPayServer/wwwroot/shopify/btcpay-shopify.js
deleted file mode 100644
index 4ae1b7b..0000000
--- a/BTCPayServer/wwwroot/shopify/btcpay-shopify.js
+++ /dev/null
@@ -1,172 +0,0 @@
-window.BTCPayShopifyIntegrationModule = function () {
- const pageElements = document.querySelector.bind(document);
- const insertElement = (document.querySelectorAll.bind(document),
- (e,
- n) => {
- n.parentNode.insertBefore(e,
- n.nextSibling)
- });
-
- // execute BTCPayShopifyIntegrationModule as soon as possible
- var paymentMethod = pageElements(".payment-method-list__item__info");
- if (null === paymentMethod) {
- return void setTimeout(() => {
- window.BTCPayShopifyIntegrationModule();
- }, 10);
- }
-
- if (!window.btcpay) {
- throw new Error("The BTCPay modal js was not loaded on this page.");
- }
- if (!window.Shopify) {
- throw new Error("The Shopify global object was not loaded on this page.");
- }
- if (!window.BTCPAYSERVER_URL || !window.STORE_ID) {
- throw new Error("The BTCPAYSERVER_URL STORE_ID global vars were not set on this page.");
- }
- const shopify_order_id = Shopify.checkout.order_id;
- const btcPayServerUrl = window.BTCPAYSERVER_URL;
- const storeId = window.STORE_ID;
- var currentInvoiceData;
- var modalShown = false;
-
- let buttonElement = null;
-
- var pageItems = {
- mainHeader: pageElements("#main-header"),
- orderConfirmed: pageElements(".os-step__title"),
- orderConfirmedDescription: pageElements(".os-step__description"),
- continueButton: pageElements(".step__footer__continue-btn"),
- checkMarkIcon: pageElements(".os-header__hanging-icon"),
- orderStatus: pageElements(".os-header__title"),
- paymentMethod: pageElements(".payment-method-list__item__info"),
- price: pageElements(".payment-due__price"),
- finalPrice: pageElements(".total-recap__final-price"),
- orderNumber: pageElements(".os-order-number"),
- };
-
- function setOrderAsPaid() {
- document.title = document.title.replace("Review and pay!", "Thank you!");
- pageItems.mainHeader.innerText = "Thank you!",
- pageItems.orderConfirmed && (pageItems.orderConfirmed.style.display = "block"),
- pageItems.orderConfirmedDescription && (pageItems.orderConfirmedDescription.style.display = "block"),
- pageItems.continueButton && (pageItems.continueButton.style.visibility = "visible"),
- pageItems.checkMarkIcon && (pageItems.checkMarkIcon.style.visibility = "visible"),
- buttonElement && (buttonElement.style.display = "none");
- }
-
- function showPaymentInstructions() {
- document.title = document.title.replace("Thank you!", "Review and pay!");
- pageItems.mainHeader && (pageItems.mainHeader.innerText = "Review and pay!"),
- pageItems.continueButton && (pageItems.continueButton.style.visibility = "hidden"),
- pageItems.checkMarkIcon && (pageItems.checkMarkIcon.style.visibility = "hidden"),
- pageItems.orderConfirmed && (pageItems.orderConfirmed.style.display = "none"),
- pageItems.orderConfirmedDescription && (pageItems.orderConfirmedDescription.style.display = "none");
- }
-
- function getOrCheckInvoice(backgroundCheck) {
- const url = btcPayServerUrl + "/stores/" + storeId + "/plugins/shopify/" + shopify_order_id+"?amount="+Shopify.checkout.payment_due+ (backgroundCheck ? "&checkonly=true" : "");
- return fetch(url, {
- method: "GET",
- mode: "cors", // no-cors, cors, *same-origin,
- headers: {
- "Content-Type": "application/json",
- "accept": "application/json",
- }
- })
- .then(function (response) {
- return response.json();
- }).catch(function () {
- if (!backgroundCheck)
- alert("Could not initiate BTCPay Server payment method, try again later.");
- })
- }
-
- function onPayButtonClicked() {
- buttonElement.innerHTML = "<span>Displaying Invoice...</span>";
-
- getOrCheckInvoice().then(handleInvoiceData).catch(fail.bind(this));
- }
-
- function handleInvoiceData(data, opts) {
- currentInvoiceData = data;
- if (!currentInvoiceData) {
- if (modalShown) {
- window.btcpay.hideFrame();
- fail();
- }else if(opts && opts.backgroundCheck){
- injectPaymentButtonHtml();
- }else{
- fail();
- }
- return;
- }
- if (["settled", "processing"].indexOf(currentInvoiceData.status.toLowerCase()) >= 0) {
- setOrderAsPaid();
- } else if (["invalid", "expired"].indexOf(currentInvoiceData.status.toLowerCase()) >= 0) {
- fail();
- } else if (!opts || !opts.backgroundCheck) {
- showModal();
- }
- }
-
- function showModal() {
- if (currentInvoiceData && !modalShown) {
- modalShown = true;
- window.btcpay.setApiUrlPrefix(btcPayServerUrl);
-
- window.btcpay.onModalReceiveMessage(function (evt) {
- if (evt && evt.invoiceId && evt.status) {
- currentInvoiceData = evt;
- }
- });
-
- window.btcpay.onModalWillEnter(function () {
- modalShown = true;
- });
-
- window.btcpay.onModalWillLeave(function () {
- modalShown = false;
- getOrCheckInvoice(true).then(function (d) {
- buttonElement.innerHTML = payButtonHtml;
- handleInvoiceData(d, {backgroundCheck: true})
- });
- });
- btcpay.appendAndShowInvoiceFrame(currentInvoiceData.invoiceId);
- }
- }
-
- function fail() {
- currentInvoiceData = null;
- buttonElement.innerHTML = payButtonHtml;
- }
-
- const payButtonHtml = '<button class="" onclick="onPayButtonClicked()" style="width:210px; border: none; outline: none;"><img src="' + btcPayServerUrl + '/img/paybutton/pay.svg"></button>';
-
- function injectPaymentButtonHtml() {
- // Payment button that opens modal
- buttonElement = document.getElementById("btcpayserver-pay");
- if (buttonElement) {
- return;
- }
- buttonElement = document.createElement("div");
- buttonElement.id = "btcpayserver-pay";
- buttonElement.innerHTML = payButtonHtml;
- insertElement(buttonElement, pageItems.orderConfirmed);
- }
-
- if (["bitcoin", "btc", "btcpayserver", "btcpay server"].filter(value => pageItems.paymentMethod.innerText.toLowerCase().indexOf(value) !== -1).length === 0) {
- return;
- }
- showPaymentInstructions();
- window.onPayButtonClicked = onPayButtonClicked.bind(this);
- getOrCheckInvoice(false).then(function (d) {
- if (d) {
- injectPaymentButtonHtml();
- handleInvoiceData(d, { backgroundCheck: true })
- }
- });
-
-};
-
-window.BTCPayShopifyIntegrationModule();
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.