This commit is contained in:
Kukks
2024-10-10 11:32:00 +02:00
parent c768d7000b
commit 9b90e10b66
108 changed files with 757 additions and 1350 deletions

View File

@@ -83,8 +83,8 @@ Global
{B19C9F52-DC47-466D-8B5C-2D202B7B003F}.Altcoins-Debug|Any CPU.Build.0 = Altcoins-Debug|Any CPU {B19C9F52-DC47-466D-8B5C-2D202B7B003F}.Altcoins-Debug|Any CPU.Build.0 = Altcoins-Debug|Any CPU
{B19C9F52-DC47-466D-8B5C-2D202B7B003F}.Altcoins-Release|Any CPU.ActiveCfg = Altcoins-Release|Any CPU {B19C9F52-DC47-466D-8B5C-2D202B7B003F}.Altcoins-Release|Any CPU.ActiveCfg = Altcoins-Release|Any CPU
{B19C9F52-DC47-466D-8B5C-2D202B7B003F}.Altcoins-Release|Any CPU.Build.0 = Altcoins-Release|Any CPU {B19C9F52-DC47-466D-8B5C-2D202B7B003F}.Altcoins-Release|Any CPU.Build.0 = Altcoins-Release|Any CPU
{B19C9F52-DC47-466D-8B5C-2D202B7B003F}.Debug|Any CPU.ActiveCfg = Altcoins-Debug|Any CPU {B19C9F52-DC47-466D-8B5C-2D202B7B003F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B19C9F52-DC47-466D-8B5C-2D202B7B003F}.Debug|Any CPU.Build.0 = Altcoins-Debug|Any CPU {B19C9F52-DC47-466D-8B5C-2D202B7B003F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AD9635BB-C70E-4676-BB04-900D51B01666}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {AD9635BB-C70E-4676-BB04-900D51B01666}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AD9635BB-C70E-4676-BB04-900D51B01666}.Debug|Any CPU.Build.0 = Debug|Any CPU {AD9635BB-C70E-4676-BB04-900D51B01666}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AD9635BB-C70E-4676-BB04-900D51B01666}.Release|Any CPU.ActiveCfg = Release|Any CPU {AD9635BB-C70E-4676-BB04-900D51B01666}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -99,48 +99,48 @@ Global
{8F158B88-0FEE-44FF-8552-7C0F17D5C508}.Altcoins-Debug|Any CPU.Build.0 = Altcoins-Debug|Any CPU {8F158B88-0FEE-44FF-8552-7C0F17D5C508}.Altcoins-Debug|Any CPU.Build.0 = Altcoins-Debug|Any CPU
{8F158B88-0FEE-44FF-8552-7C0F17D5C508}.Altcoins-Release|Any CPU.ActiveCfg = Altcoins-Release|Any CPU {8F158B88-0FEE-44FF-8552-7C0F17D5C508}.Altcoins-Release|Any CPU.ActiveCfg = Altcoins-Release|Any CPU
{8F158B88-0FEE-44FF-8552-7C0F17D5C508}.Altcoins-Release|Any CPU.Build.0 = Altcoins-Release|Any CPU {8F158B88-0FEE-44FF-8552-7C0F17D5C508}.Altcoins-Release|Any CPU.Build.0 = Altcoins-Release|Any CPU
{8F158B88-0FEE-44FF-8552-7C0F17D5C508}.Debug|Any CPU.ActiveCfg = Altcoins-Debug|Any CPU {8F158B88-0FEE-44FF-8552-7C0F17D5C508}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8F158B88-0FEE-44FF-8552-7C0F17D5C508}.Debug|Any CPU.Build.0 = Altcoins-Debug|Any CPU {8F158B88-0FEE-44FF-8552-7C0F17D5C508}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DF85EFA4-0EF5-4A99-853F-E6F9C88E3F8C}.Release|Any CPU.ActiveCfg = Release|Any CPU {DF85EFA4-0EF5-4A99-853F-E6F9C88E3F8C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DF85EFA4-0EF5-4A99-853F-E6F9C88E3F8C}.Release|Any CPU.Build.0 = Release|Any CPU {DF85EFA4-0EF5-4A99-853F-E6F9C88E3F8C}.Release|Any CPU.Build.0 = Release|Any CPU
{DF85EFA4-0EF5-4A99-853F-E6F9C88E3F8C}.Altcoins-Debug|Any CPU.ActiveCfg = Altcoins-Debug|Any CPU {DF85EFA4-0EF5-4A99-853F-E6F9C88E3F8C}.Altcoins-Debug|Any CPU.ActiveCfg = Altcoins-Debug|Any CPU
{DF85EFA4-0EF5-4A99-853F-E6F9C88E3F8C}.Altcoins-Debug|Any CPU.Build.0 = Altcoins-Debug|Any CPU {DF85EFA4-0EF5-4A99-853F-E6F9C88E3F8C}.Altcoins-Debug|Any CPU.Build.0 = Altcoins-Debug|Any CPU
{DF85EFA4-0EF5-4A99-853F-E6F9C88E3F8C}.Altcoins-Release|Any CPU.ActiveCfg = Altcoins-Release|Any CPU {DF85EFA4-0EF5-4A99-853F-E6F9C88E3F8C}.Altcoins-Release|Any CPU.ActiveCfg = Altcoins-Release|Any CPU
{DF85EFA4-0EF5-4A99-853F-E6F9C88E3F8C}.Altcoins-Release|Any CPU.Build.0 = Altcoins-Release|Any CPU {DF85EFA4-0EF5-4A99-853F-E6F9C88E3F8C}.Altcoins-Release|Any CPU.Build.0 = Altcoins-Release|Any CPU
{DF85EFA4-0EF5-4A99-853F-E6F9C88E3F8C}.Debug|Any CPU.ActiveCfg = Altcoins-Debug|Any CPU {DF85EFA4-0EF5-4A99-853F-E6F9C88E3F8C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DF85EFA4-0EF5-4A99-853F-E6F9C88E3F8C}.Debug|Any CPU.Build.0 = Altcoins-Debug|Any CPU {DF85EFA4-0EF5-4A99-853F-E6F9C88E3F8C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2C5C4DF9-BA1F-4671-9F24-B22D7C9C3D21}.Release|Any CPU.ActiveCfg = Release|Any CPU {2C5C4DF9-BA1F-4671-9F24-B22D7C9C3D21}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2C5C4DF9-BA1F-4671-9F24-B22D7C9C3D21}.Release|Any CPU.Build.0 = Release|Any CPU {2C5C4DF9-BA1F-4671-9F24-B22D7C9C3D21}.Release|Any CPU.Build.0 = Release|Any CPU
{2C5C4DF9-BA1F-4671-9F24-B22D7C9C3D21}.Altcoins-Debug|Any CPU.ActiveCfg = Altcoins-Debug|Any CPU {2C5C4DF9-BA1F-4671-9F24-B22D7C9C3D21}.Altcoins-Debug|Any CPU.ActiveCfg = Altcoins-Debug|Any CPU
{2C5C4DF9-BA1F-4671-9F24-B22D7C9C3D21}.Altcoins-Debug|Any CPU.Build.0 = Altcoins-Debug|Any CPU {2C5C4DF9-BA1F-4671-9F24-B22D7C9C3D21}.Altcoins-Debug|Any CPU.Build.0 = Altcoins-Debug|Any CPU
{2C5C4DF9-BA1F-4671-9F24-B22D7C9C3D21}.Altcoins-Release|Any CPU.ActiveCfg = Altcoins-Release|Any CPU {2C5C4DF9-BA1F-4671-9F24-B22D7C9C3D21}.Altcoins-Release|Any CPU.ActiveCfg = Altcoins-Release|Any CPU
{2C5C4DF9-BA1F-4671-9F24-B22D7C9C3D21}.Altcoins-Release|Any CPU.Build.0 = Altcoins-Release|Any CPU {2C5C4DF9-BA1F-4671-9F24-B22D7C9C3D21}.Altcoins-Release|Any CPU.Build.0 = Altcoins-Release|Any CPU
{2C5C4DF9-BA1F-4671-9F24-B22D7C9C3D21}.Debug|Any CPU.ActiveCfg = Altcoins-Debug|Any CPU {2C5C4DF9-BA1F-4671-9F24-B22D7C9C3D21}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2C5C4DF9-BA1F-4671-9F24-B22D7C9C3D21}.Debug|Any CPU.Build.0 = Altcoins-Debug|Any CPU {2C5C4DF9-BA1F-4671-9F24-B22D7C9C3D21}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D7E7309D-C4F4-496A-B2C8-BC5D3991B9C0}.Release|Any CPU.ActiveCfg = Release|Any CPU {D7E7309D-C4F4-496A-B2C8-BC5D3991B9C0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D7E7309D-C4F4-496A-B2C8-BC5D3991B9C0}.Release|Any CPU.Build.0 = Release|Any CPU {D7E7309D-C4F4-496A-B2C8-BC5D3991B9C0}.Release|Any CPU.Build.0 = Release|Any CPU
{D7E7309D-C4F4-496A-B2C8-BC5D3991B9C0}.Altcoins-Debug|Any CPU.ActiveCfg = Altcoins-Debug|Any CPU {D7E7309D-C4F4-496A-B2C8-BC5D3991B9C0}.Altcoins-Debug|Any CPU.ActiveCfg = Altcoins-Debug|Any CPU
{D7E7309D-C4F4-496A-B2C8-BC5D3991B9C0}.Altcoins-Debug|Any CPU.Build.0 = Altcoins-Debug|Any CPU {D7E7309D-C4F4-496A-B2C8-BC5D3991B9C0}.Altcoins-Debug|Any CPU.Build.0 = Altcoins-Debug|Any CPU
{D7E7309D-C4F4-496A-B2C8-BC5D3991B9C0}.Altcoins-Release|Any CPU.ActiveCfg = Altcoins-Release|Any CPU {D7E7309D-C4F4-496A-B2C8-BC5D3991B9C0}.Altcoins-Release|Any CPU.ActiveCfg = Altcoins-Release|Any CPU
{D7E7309D-C4F4-496A-B2C8-BC5D3991B9C0}.Altcoins-Release|Any CPU.Build.0 = Altcoins-Release|Any CPU {D7E7309D-C4F4-496A-B2C8-BC5D3991B9C0}.Altcoins-Release|Any CPU.Build.0 = Altcoins-Release|Any CPU
{D7E7309D-C4F4-496A-B2C8-BC5D3991B9C0}.Debug|Any CPU.ActiveCfg = Altcoins-Debug|Any CPU {D7E7309D-C4F4-496A-B2C8-BC5D3991B9C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D7E7309D-C4F4-496A-B2C8-BC5D3991B9C0}.Debug|Any CPU.Build.0 = Altcoins-Debug|Any CPU {D7E7309D-C4F4-496A-B2C8-BC5D3991B9C0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3F2E0BA0-9EA7-490F-894D-F9703F35B174}.Release|Any CPU.ActiveCfg = Release|Any CPU {3F2E0BA0-9EA7-490F-894D-F9703F35B174}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3F2E0BA0-9EA7-490F-894D-F9703F35B174}.Release|Any CPU.Build.0 = Release|Any CPU {3F2E0BA0-9EA7-490F-894D-F9703F35B174}.Release|Any CPU.Build.0 = Release|Any CPU
{3F2E0BA0-9EA7-490F-894D-F9703F35B174}.Altcoins-Debug|Any CPU.ActiveCfg = Altcoins-Debug|Any CPU {3F2E0BA0-9EA7-490F-894D-F9703F35B174}.Altcoins-Debug|Any CPU.ActiveCfg = Altcoins-Debug|Any CPU
{3F2E0BA0-9EA7-490F-894D-F9703F35B174}.Altcoins-Debug|Any CPU.Build.0 = Altcoins-Debug|Any CPU {3F2E0BA0-9EA7-490F-894D-F9703F35B174}.Altcoins-Debug|Any CPU.Build.0 = Altcoins-Debug|Any CPU
{3F2E0BA0-9EA7-490F-894D-F9703F35B174}.Altcoins-Release|Any CPU.ActiveCfg = Altcoins-Release|Any CPU {3F2E0BA0-9EA7-490F-894D-F9703F35B174}.Altcoins-Release|Any CPU.ActiveCfg = Altcoins-Release|Any CPU
{3F2E0BA0-9EA7-490F-894D-F9703F35B174}.Altcoins-Release|Any CPU.Build.0 = Altcoins-Release|Any CPU {3F2E0BA0-9EA7-490F-894D-F9703F35B174}.Altcoins-Release|Any CPU.Build.0 = Altcoins-Release|Any CPU
{3F2E0BA0-9EA7-490F-894D-F9703F35B174}.Debug|Any CPU.ActiveCfg = Altcoins-Debug|Any CPU {3F2E0BA0-9EA7-490F-894D-F9703F35B174}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3F2E0BA0-9EA7-490F-894D-F9703F35B174}.Debug|Any CPU.Build.0 = Altcoins-Debug|Any CPU {3F2E0BA0-9EA7-490F-894D-F9703F35B174}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6295533A-F941-40CA-B889-FE6C0432ED53}.Release|Any CPU.ActiveCfg = Release|Any CPU {6295533A-F941-40CA-B889-FE6C0432ED53}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6295533A-F941-40CA-B889-FE6C0432ED53}.Release|Any CPU.Build.0 = Release|Any CPU {6295533A-F941-40CA-B889-FE6C0432ED53}.Release|Any CPU.Build.0 = Release|Any CPU
{6295533A-F941-40CA-B889-FE6C0432ED53}.Altcoins-Debug|Any CPU.ActiveCfg = Altcoins-Debug|Any CPU {6295533A-F941-40CA-B889-FE6C0432ED53}.Altcoins-Debug|Any CPU.ActiveCfg = Altcoins-Debug|Any CPU
{6295533A-F941-40CA-B889-FE6C0432ED53}.Altcoins-Debug|Any CPU.Build.0 = Altcoins-Debug|Any CPU {6295533A-F941-40CA-B889-FE6C0432ED53}.Altcoins-Debug|Any CPU.Build.0 = Altcoins-Debug|Any CPU
{6295533A-F941-40CA-B889-FE6C0432ED53}.Altcoins-Release|Any CPU.ActiveCfg = Altcoins-Release|Any CPU {6295533A-F941-40CA-B889-FE6C0432ED53}.Altcoins-Release|Any CPU.ActiveCfg = Altcoins-Release|Any CPU
{6295533A-F941-40CA-B889-FE6C0432ED53}.Altcoins-Release|Any CPU.Build.0 = Altcoins-Release|Any CPU {6295533A-F941-40CA-B889-FE6C0432ED53}.Altcoins-Release|Any CPU.Build.0 = Altcoins-Release|Any CPU
{6295533A-F941-40CA-B889-FE6C0432ED53}.Debug|Any CPU.ActiveCfg = Altcoins-Debug|Any CPU {6295533A-F941-40CA-B889-FE6C0432ED53}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6295533A-F941-40CA-B889-FE6C0432ED53}.Debug|Any CPU.Build.0 = Altcoins-Debug|Any CPU {6295533A-F941-40CA-B889-FE6C0432ED53}.Debug|Any CPU.Build.0 = Debug|Any CPU
{58863D86-3C78-4BEC-ACB6-2F82CC141210}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {58863D86-3C78-4BEC-ACB6-2F82CC141210}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{58863D86-3C78-4BEC-ACB6-2F82CC141210}.Debug|Any CPU.Build.0 = Debug|Any CPU {58863D86-3C78-4BEC-ACB6-2F82CC141210}.Debug|Any CPU.Build.0 = Debug|Any CPU
{58863D86-3C78-4BEC-ACB6-2F82CC141210}.Release|Any CPU.ActiveCfg = Release|Any CPU {58863D86-3C78-4BEC-ACB6-2F82CC141210}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -155,8 +155,8 @@ Global
{B4E2ED08-4AD3-4648-8BDB-3107200460B9}.Altcoins-Debug|Any CPU.Build.0 = Altcoins-Debug|Any CPU {B4E2ED08-4AD3-4648-8BDB-3107200460B9}.Altcoins-Debug|Any CPU.Build.0 = Altcoins-Debug|Any CPU
{B4E2ED08-4AD3-4648-8BDB-3107200460B9}.Altcoins-Release|Any CPU.ActiveCfg = Altcoins-Release|Any CPU {B4E2ED08-4AD3-4648-8BDB-3107200460B9}.Altcoins-Release|Any CPU.ActiveCfg = Altcoins-Release|Any CPU
{B4E2ED08-4AD3-4648-8BDB-3107200460B9}.Altcoins-Release|Any CPU.Build.0 = Altcoins-Release|Any CPU {B4E2ED08-4AD3-4648-8BDB-3107200460B9}.Altcoins-Release|Any CPU.Build.0 = Altcoins-Release|Any CPU
{B4E2ED08-4AD3-4648-8BDB-3107200460B9}.Debug|Any CPU.ActiveCfg = Altcoins-Debug|Any CPU {B4E2ED08-4AD3-4648-8BDB-3107200460B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B4E2ED08-4AD3-4648-8BDB-3107200460B9}.Debug|Any CPU.Build.0 = Altcoins-Debug|Any CPU {B4E2ED08-4AD3-4648-8BDB-3107200460B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5E1BAA06-7828-47BC-89D6-19C2A78EA427}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5E1BAA06-7828-47BC-89D6-19C2A78EA427}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5E1BAA06-7828-47BC-89D6-19C2A78EA427}.Debug|Any CPU.Build.0 = Debug|Any CPU {5E1BAA06-7828-47BC-89D6-19C2A78EA427}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5E1BAA06-7828-47BC-89D6-19C2A78EA427}.Release|Any CPU.ActiveCfg = Release|Any CPU {5E1BAA06-7828-47BC-89D6-19C2A78EA427}.Release|Any CPU.ActiveCfg = Release|Any CPU

View File

@@ -1,7 +1,21 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> <wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=0D438B7D_002DF996_002D4BF3_002D8F54_002D02CB9DF120D8_002Ff_003ABTCPayCoinjoinCoinSelector_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=0D438B7D_002DF996_002D4BF3_002D8F54_002D02CB9DF120D8_002Ff_003ABTCPayCoinjoinCoinSelector_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=0D438B7D_002DF996_002D4BF3_002D8F54_002D02CB9DF120D8_002Ff_003ABTCPayWallet_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=0D438B7D_002DF996_002D4BF3_002D8F54_002D02CB9DF120D8_002Ff_003ABTCPayWallet_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ABreezEvent_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FUsers_003Fevilk_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F19b2ba64aa6844efb3312f86dbfe1b9652000_003F63_003F83f9cdbb_003FBreezEvent_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ABreezSdkLiquidMethods_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FUsers_003Fevilk_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F4dbc7b5c57d04ec8a063235af9eb203a3fc00_003F7e_003F6a527844_003FBreezSdkLiquidMethods_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ABreez_002ESdk_002ELiquid_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FUsers_003Fevilk_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FDecompilerCache_003Fmetadata_003F4dbc7b5c57d04ec8a063235af9eb203a3fc00_003F58_003F0584d9dc_003FBreez_002ESdk_002ELiquid_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ABreez_002ESdk_002ELiquid_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FUsers_003Fevilk_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FDecompilerCache_003Fmetadata_003F4dbc7b5c57d04ec8a063235af9eb203a3fc00_003F58_003F0584d9dc_003FBreez_002ESdk_002ELiquid_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ACallSiteFactory_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FUsers_003Fevilk_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003Ff7e838f3c047ee76488fa4c2b9b1bffb2b33b04a2780de4664de937e024e97_003FCallSiteFactory_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ADbSet_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FUsers_003Fevilk_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003F4b21fc3f8a3abb5d25f06c622276fbec0fd3932f169cc6af520bf5abc6a46b4_003FDbSet_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AExceptionDispatchInfo_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FUsers_003Fevilk_003FAppData_003FLocal_003FSymbols_003Fsrc_003Fdotnet_003Fruntime_003F08338fcaa5c9b9a8190abb99222fed12aaba956c_003Fsrc_003Flibraries_003FSystem_002EPrivate_002ECoreLib_003Fsrc_003FSystem_003FRuntime_003FExceptionServices_003FExceptionDispatchInfo_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AExceptionDispatchInfo_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FUsers_003Fevilk_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003Fbd1d5c50194fea68ff3559c160230b0ab50f5acf4ce3061bffd6d62958e2182_003FExceptionDispatchInfo_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ANostrClientPool_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FUsers_003Fevilk_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003F758d2546736f41ac5ee3998f9abad1f58b82254824f2532c89ddf667ed74_003FNostrClientPool_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ANostrEventTag_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FUsers_003Fevilk_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003Ff2e2e45db36b5d75d466cd5c6134e5ce566eface233da2ec472ac018f2eb16_003FNostrEventTag_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ANullable_00601_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FUsers_003Fevilk_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F2a3105e7355248488912e34f9ff5cbebca718_003F8b_003Fd6fc9706_003FNullable_00601_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ANullReferenceException_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FUsers_003Fevilk_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003Fc092928882a17a51a62a367685d90a0a613eb91fb453b9f50147e19c1f3ff40_003FNullReferenceException_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003APaymentDetails_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FUsers_003Fevilk_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F4dbc7b5c57d04ec8a063235af9eb203a3fc00_003Fc6_003Fb76207e1_003FPaymentDetails_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003APayment_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FUsers_003Fevilk_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F4dbc7b5c57d04ec8a063235af9eb203a3fc00_003F5b_003F56fb0a4a_003FPayment_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AToCollection_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FUsers_003Fevilk_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003Fbd47ee1ab214a7674f381fb716d273cc4bb64441375b47417e9dcd5ec96f89_003FToCollection_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AWhere_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FUsers_003Fevilk_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003F767db1e17d404f86c3c73c54c6b20b5ff9ec43b79ef47681ae34b40a6852cf9_003FWhere_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=D1D1116C_002D38F9_002D4EA3_002DAC65_002DA75FEA82E5C8_002Fd_003ABlockchain_002Fd_003ATransactionOutputs_002Ff_003ACoinsView_002Ecs/@EntryIndexedValue">ExplicitlyExcluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=D1D1116C_002D38F9_002D4EA3_002DAC65_002DA75FEA82E5C8_002Fd_003ABlockchain_002Fd_003ATransactionOutputs_002Ff_003ACoinsView_002Ecs/@EntryIndexedValue">ExplicitlyExcluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=D1D1116C_002D38F9_002D4EA3_002DAC65_002DA75FEA82E5C8_002Fd_003ABlockchain_002Fd_003ATransactionOutputs_002Ff_003ASmartCoin_002Ecs/@EntryIndexedValue">ExplicitlyExcluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=D1D1116C_002D38F9_002D4EA3_002DAC65_002DA75FEA82E5C8_002Fd_003ABlockchain_002Fd_003ATransactionOutputs_002Ff_003ASmartCoin_002Ecs/@EntryIndexedValue">ExplicitlyExcluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=D1D1116C_002D38F9_002D4EA3_002DAC65_002DA75FEA82E5C8_002Fd_003AWabiSabi_002Fd_003AClient_002Ff_003ACoinJoinClient_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=D1D1116C_002D38F9_002D4EA3_002DAC65_002DA75FEA82E5C8_002Fd_003AWabiSabi_002Fd_003AClient_002Ff_003ACoinJoinClient_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
@@ -50,6 +64,7 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=Nostr/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary> <s:Boolean x:Key="/Default/UserDictionary/Words/=Nostr/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

View File

@@ -10,7 +10,7 @@
<PropertyGroup> <PropertyGroup>
<Product>Bitcoin Whitepaper</Product> <Product>Bitcoin Whitepaper</Product>
<Description>This makes the Bitcoin whitepaper available on your BTCPay Server.</Description> <Description>This makes the Bitcoin whitepaper available on your BTCPay Server.</Description>
<Version>1.0.4</Version> <Version>1.0.5</Version>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup> </PropertyGroup>
<!-- Plugin development properties --> <!-- Plugin development properties -->

View File

@@ -7,7 +7,7 @@ namespace BTCPayServer.Plugins.BitcoinWhitepaper
{ {
public override IBTCPayServerPlugin.PluginDependency[] Dependencies { get; } = public override IBTCPayServerPlugin.PluginDependency[] Dependencies { get; } =
{ {
new() { Identifier = nameof(BTCPayServer), Condition = ">=1.12.0" } new() { Identifier = nameof(BTCPayServer), Condition = ">=2.0.0" }
}; };
} }
} }

View File

@@ -9,7 +9,7 @@
<PropertyGroup> <PropertyGroup>
<Product>Blink</Product> <Product>Blink</Product>
<Description>Blink Lightning support</Description> <Description>Blink Lightning support</Description>
<Version>1.0.8</Version> <Version>1.0.9</Version>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<RootNamespace>BTCPayServer.Plugins.Blink</RootNamespace> <RootNamespace>BTCPayServer.Plugins.Blink</RootNamespace>
</PropertyGroup> </PropertyGroup>

View File

@@ -12,7 +12,7 @@ namespace BTCPayServer.Plugins.Blink
{ {
public override IBTCPayServerPlugin.PluginDependency[] Dependencies { get; } = public override IBTCPayServerPlugin.PluginDependency[] Dependencies { get; } =
{ {
new() {Identifier = nameof(BTCPayServer), Condition = ">=1.12.0"} new() { Identifier = nameof(BTCPayServer), Condition = ">=2.0.0" }
}; };

View File

@@ -9,7 +9,7 @@
<PropertyGroup> <PropertyGroup>
<Product>Breez / Greenlight</Product> <Product>Breez / Greenlight</Product>
<Description>Lightweight lightning baby!</Description> <Description>Lightweight lightning baby!</Description>
<Version>1.0.8</Version> <Version>1.0.9</Version>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup> </PropertyGroup>
<!-- Plugin development properties --> <!-- Plugin development properties -->
@@ -34,7 +34,7 @@
<ProjectReference Include="..\..\submodules\btcpayserver\BTCPayServer\BTCPayServer.csproj" /> <ProjectReference Include="..\..\submodules\btcpayserver\BTCPayServer\BTCPayServer.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Breez.Sdk" Version="0.5.2" /> <PackageReference Include="Breez.Sdk" Version="0.6.1" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -2,7 +2,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.IO.Compression; using System.IO.Compression;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Breez.Sdk; using Breez.Sdk;
using BTCPayServer.Abstractions.Constants; using BTCPayServer.Abstractions.Constants;
@@ -12,13 +11,13 @@ using BTCPayServer.Lightning;
using BTCPayServer.Models; using BTCPayServer.Models;
using BTCPayServer.Payments; using BTCPayServer.Payments;
using BTCPayServer.Payments.Lightning; using BTCPayServer.Payments.Lightning;
using BTCPayServer.Services.Invoices;
using BTCPayServer.Services.Stores; using BTCPayServer.Services.Stores;
using BTCPayServer.Services.Wallets; using BTCPayServer.Services.Wallets;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using NBitcoin; using NBitcoin;
using NBitcoin.DataEncoders; using NBitcoin.DataEncoders;
using NBXplorer.DerivationStrategy;
namespace BTCPayServer.Plugins.Breez; namespace BTCPayServer.Plugins.Breez;
@@ -26,15 +25,19 @@ namespace BTCPayServer.Plugins.Breez;
[Route("plugins/{storeId}/Breez")] [Route("plugins/{storeId}/Breez")]
public class BreezController : Controller public class BreezController : Controller
{ {
private readonly PaymentMethodHandlerDictionary _paymentMethodHandlerDictionary;
private readonly BTCPayNetworkProvider _btcPayNetworkProvider; private readonly BTCPayNetworkProvider _btcPayNetworkProvider;
private readonly BreezService _breezService; private readonly BreezService _breezService;
private readonly BTCPayWalletProvider _btcWalletProvider; private readonly BTCPayWalletProvider _btcWalletProvider;
private readonly StoreRepository _storeRepository; private readonly StoreRepository _storeRepository;
public BreezController(BTCPayNetworkProvider btcPayNetworkProvider, public BreezController(
PaymentMethodHandlerDictionary paymentMethodHandlerDictionary,
BTCPayNetworkProvider btcPayNetworkProvider,
BreezService breezService, BreezService breezService,
BTCPayWalletProvider btcWalletProvider, StoreRepository storeRepository) BTCPayWalletProvider btcWalletProvider, StoreRepository storeRepository)
{ {
_paymentMethodHandlerDictionary = paymentMethodHandlerDictionary;
_btcPayNetworkProvider = btcPayNetworkProvider; _btcPayNetworkProvider = btcPayNetworkProvider;
_breezService = breezService; _breezService = breezService;
_btcWalletProvider = btcWalletProvider; _btcWalletProvider = btcWalletProvider;
@@ -113,7 +116,7 @@ public class BreezController : Controller
if (address.Equals("store", StringComparison.InvariantCultureIgnoreCase)) if (address.Equals("store", StringComparison.InvariantCultureIgnoreCase))
{ {
var store = ControllerContext.HttpContext.GetStoreData() var store = ControllerContext.HttpContext.GetStoreData()
.GetDerivationSchemeSettings(_btcPayNetworkProvider, "BTC"); .GetDerivationSchemeSettings(_paymentMethodHandlerDictionary, "BTC");
var res = await _btcWalletProvider.GetWallet(storeId) var res = await _btcWalletProvider.GetWallet(storeId)
.ReserveAddressAsync(storeId, store.AccountDerivation, "Breez"); .ReserveAddressAsync(storeId, store.AccountDerivation, "Breez");
address = res.Address.ToString(); address = res.Address.ToString();
@@ -264,7 +267,7 @@ public class BreezController : Controller
if (address.Equals("store", StringComparison.InvariantCultureIgnoreCase)) if (address.Equals("store", StringComparison.InvariantCultureIgnoreCase))
{ {
var store = ControllerContext.HttpContext.GetStoreData() var store = ControllerContext.HttpContext.GetStoreData()
.GetDerivationSchemeSettings(_btcPayNetworkProvider, "BTC"); .GetDerivationSchemeSettings(_paymentMethodHandlerDictionary, "BTC");
var res = await _btcWalletProvider.GetWallet(storeId) var res = await _btcWalletProvider.GetWallet(storeId)
.ReserveAddressAsync(storeId, store.AccountDerivation, "Breez"); .ReserveAddressAsync(storeId, store.AccountDerivation, "Breez");
address = res.Address.ToString(); address = res.Address.ToString();
@@ -342,10 +345,8 @@ public class BreezController : Controller
public async Task<IActionResult> Configure(string storeId, string command, BreezSettings settings) public async Task<IActionResult> Configure(string storeId, string command, BreezSettings settings)
{ {
var store = HttpContext.GetStoreData(); var store = HttpContext.GetStoreData();
var existing = store.GetSupportedPaymentMethods(_btcPayNetworkProvider).OfType<LightningSupportedPaymentMethod>() var pmi = PaymentTypes.LN.GetPaymentMethodId("BTC");
.FirstOrDefault(method => var existing = store.GetPaymentMethodConfig<LightningPaymentMethodConfig>(pmi, _paymentMethodHandlerDictionary);
method.PaymentId.PaymentType == LightningPaymentType.Instance &&
method.PaymentId.CryptoCode == "BTC");
if (command == "clear") if (command == "clear")
{ {
@@ -355,7 +356,7 @@ public class BreezController : Controller
var isStoreSetToThisMicro = existing?.GetExternalLightningUrl() == client?.ToString(); var isStoreSetToThisMicro = existing?.GetExternalLightningUrl() == client?.ToString();
if (client is not null && isStoreSetToThisMicro) if (client is not null && isStoreSetToThisMicro)
{ {
store.SetSupportedPaymentMethod(existing.PaymentId, null); store.SetPaymentMethodConfig(_paymentMethodHandlerDictionary[pmi], null);
await _storeRepository.UpdateStore(store); await _storeRepository.UpdateStore(store);
} }
return RedirectToAction(nameof(Configure), new {storeId}); return RedirectToAction(nameof(Configure), new {storeId});
@@ -422,18 +423,13 @@ public class BreezController : Controller
if(existing is null) if(existing is null)
{ {
existing = new LightningSupportedPaymentMethod()
{ existing = new LightningPaymentMethodConfig();
CryptoCode = "BTC"
};
var client = _breezService.GetClient(storeId); var client = _breezService.GetClient(storeId);
existing.SetLightningUrl(client); existing.SetLightningUrl(client);
store.SetSupportedPaymentMethod(existing); store.SetPaymentMethodConfig(_paymentMethodHandlerDictionary[pmi], existing);
var lnurl = new LNURLPaySupportedPaymentMethod() var lnurlPMI = PaymentTypes.LNURL.GetPaymentMethodId("BTC");
{ store.SetPaymentMethodConfig(_paymentMethodHandlerDictionary[lnurlPMI], new LNURLPaymentMethodConfig());
CryptoCode = "BTC",
};
store.SetSupportedPaymentMethod(lnurl);
await _storeRepository.UpdateStore(store); await _storeRepository.UpdateStore(store);
} }

View File

@@ -252,7 +252,7 @@ public class BreezLightningClient : ILightningClient, IDisposable, EventListener
OffchainBalance = new OffchainBalance() OffchainBalance = new OffchainBalance()
{ {
Local = LightMoney.MilliSatoshis(ni.channelsBalanceMsat), Local = LightMoney.MilliSatoshis(ni.channelsBalanceMsat),
Remote = LightMoney.MilliSatoshis(ni.inboundLiquidityMsats), Remote = LightMoney.MilliSatoshis(ni.totalInboundLiquidityMsats),
} }
}; };
} }
@@ -275,7 +275,7 @@ public class BreezLightningClient : ILightningClient, IDisposable, EventListener
} }
else else
{ {
result = Sdk.SendPayment(new SendPaymentRequest(bolt11, (ulong?) payParams.Amount?.MilliSatoshi)); result = Sdk.SendPayment(new SendPaymentRequest(bolt11,false, (ulong?) payParams.Amount?.MilliSatoshi));
} }
var details = result.payment.details as PaymentDetails.Ln; var details = result.payment.details as PaymentDetails.Ln;

View File

@@ -12,7 +12,7 @@ namespace BTCPayServer.Plugins.Breez
{ {
public override IBTCPayServerPlugin.PluginDependency[] Dependencies { get; } = public override IBTCPayServerPlugin.PluginDependency[] Dependencies { get; } =
{ {
new() { Identifier = nameof(BTCPayServer), Condition = ">=1.13.2" } new() { Identifier = nameof(BTCPayServer), Condition = ">=2.0.0" }
}; };
public override void Execute(IServiceCollection applicationBuilder) public override void Execute(IServiceCollection applicationBuilder)

View File

@@ -11,7 +11,9 @@ using BTCPayServer.Events;
using BTCPayServer.HostedServices; using BTCPayServer.HostedServices;
using BTCPayServer.Payments; using BTCPayServer.Payments;
using BTCPayServer.Payments.Lightning; using BTCPayServer.Payments.Lightning;
using BTCPayServer.Services.Invoices;
using BTCPayServer.Services.Stores; using BTCPayServer.Services.Stores;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using NBitcoin; using NBitcoin;
@@ -22,7 +24,8 @@ public class BreezService:EventHostedServiceBase
{ {
private readonly StoreRepository _storeRepository; private readonly StoreRepository _storeRepository;
private readonly IOptions<DataDirectories> _dataDirectories; private readonly IOptions<DataDirectories> _dataDirectories;
private readonly BTCPayNetworkProvider _btcPayNetworkProvider; private readonly IServiceProvider _serviceProvider;
private PaymentMethodHandlerDictionary _paymentMethodHandlerDictionary => _serviceProvider.GetRequiredService<PaymentMethodHandlerDictionary>();
private readonly ILogger _logger; private readonly ILogger _logger;
private Dictionary<string, BreezSettings> _settings; private Dictionary<string, BreezSettings> _settings;
private Dictionary<string, BreezLightningClient> _clients = new(); private Dictionary<string, BreezLightningClient> _clients = new();
@@ -31,12 +34,12 @@ public class BreezService:EventHostedServiceBase
EventAggregator eventAggregator, EventAggregator eventAggregator,
StoreRepository storeRepository, StoreRepository storeRepository,
IOptions<DataDirectories> dataDirectories, IOptions<DataDirectories> dataDirectories,
BTCPayNetworkProvider btcPayNetworkProvider, IServiceProvider serviceProvider,
ILogger<BreezService> logger) : base(eventAggregator, logger) ILogger<BreezService> logger) : base(eventAggregator, logger)
{ {
_storeRepository = storeRepository; _storeRepository = storeRepository;
_dataDirectories = dataDirectories; _dataDirectories = dataDirectories;
_btcPayNetworkProvider = btcPayNetworkProvider; _serviceProvider = serviceProvider;
_logger = logger; _logger = logger;
} }
@@ -133,13 +136,13 @@ public class BreezService:EventHostedServiceBase
{ {
_settings.Remove(storeId, out var oldSettings ); _settings.Remove(storeId, out var oldSettings );
var data = await _storeRepository.FindStore(storeId); var data = await _storeRepository.FindStore(storeId);
var existing = data?.GetSupportedPaymentMethods(_btcPayNetworkProvider) var pmi = PaymentTypes.LN.GetPaymentMethodId("BTC");
.OfType<LightningSupportedPaymentMethod>().FirstOrDefault(method => var existing =
method.CryptoCode == "BTC" && method.PaymentId.PaymentType == LightningPaymentType.Instance); data?.GetPaymentMethodConfig<LightningPaymentMethodConfig>(pmi, _paymentMethodHandlerDictionary);
var isBreez = existing?.GetExternalLightningUrl() == $"type=breez;key={oldSettings.PaymentKey}"; var isBreez = existing?.GetExternalLightningUrl() == $"type=breez;key={oldSettings.PaymentKey}";
if (isBreez) if (isBreez)
{ {
data.SetSupportedPaymentMethod(new PaymentMethodId("BTC", LightningPaymentType.Instance), null ); data.SetPaymentMethodConfig(_paymentMethodHandlerDictionary[pmi], null );
await _storeRepository.UpdateStore(data); await _storeRepository.UpdateStore(data);
} }
Directory.Delete(GetWorkDir(storeId), true); Directory.Delete(GetWorkDir(storeId), true);

View File

@@ -1,7 +1,5 @@
@using Breez.Sdk @using Breez.Sdk
@using BTCPayServer @using BTCPayServer
@using BTCPayServer.Abstractions.Extensions
@using BTCPayServer.Abstractions.Contracts
@using BTCPayServer.Client @using BTCPayServer.Client
@using BTCPayServer.Components.QRCode @using BTCPayServer.Components.QRCode
@using BTCPayServer.Components.TruncateCenter @using BTCPayServer.Components.TruncateCenter
@@ -10,13 +8,15 @@
@using BTCPayServer.Plugins.Breez @using BTCPayServer.Plugins.Breez
@using BTCPayServer.Security @using BTCPayServer.Security
@using BTCPayServer.Services @using BTCPayServer.Services
@using BTCPayServer.Services.Invoices
@using Microsoft.AspNetCore.Mvc.TagHelpers @using Microsoft.AspNetCore.Mvc.TagHelpers
@using NBitcoin @using NBitcoin
@inject BreezService BreezService @inject BreezService BreezService
@inject BTCPayNetworkProvider BtcPayNetworkProvider
@inject TransactionLinkProviders TransactionLinkProviders @inject TransactionLinkProviders TransactionLinkProviders
@inject PaymentMethodHandlerDictionary PaymentMethodHandlerDictionary
@{ @{
ViewData.SetActivePage("Breez", "Swap In", "SwapIn"); ViewData.SetActivePage("Breez", "Swap In", "SwapIn");
var pmi = PaymentTypes.CHAIN.GetPaymentMethodId("BTC");
string storeId = Model switch string storeId = Model switch
{ {
string s => s, string s => s,
@@ -38,10 +38,9 @@
} }
var refundables = sdk.ListRefundables(); var refundables = sdk.ListRefundables();
var deriv = Context.GetStoreData().GetDerivationSchemeSettings(BtcPayNetworkProvider, "BTC"); var deriv = Context.GetStoreData().GetDerivationSchemeSettings(PaymentMethodHandlerDictionary, "BTC");
var ni = sdk.NodeInfo(); var ni = sdk.NodeInfo();
var f = sdk.RecommendedFees(); var f = sdk.RecommendedFees();
var pmi = new PaymentMethodId("BTC", PaymentTypes.BTCLike);
} }
<datalist id="fees"> <datalist id="fees">

View File

@@ -1,18 +1,18 @@
@using BTCPayServer @using BTCPayServer
@using BTCPayServer.Abstractions.Extensions
@using BTCPayServer.Plugins.Breez @using BTCPayServer.Plugins.Breez
@using BTCPayServer.Security @using BTCPayServer.Security
@using BTCPayServer.Services.Invoices
@using Microsoft.AspNetCore.Mvc.TagHelpers @using Microsoft.AspNetCore.Mvc.TagHelpers
@using Microsoft.AspNetCore.Routing @using Microsoft.AspNetCore.Routing
@model string @model string
@inject BreezService BreezService @inject BreezService BreezService
@inject BTCPayNetworkProvider BtcPayNetworkProvider @inject PaymentMethodHandlerDictionary PaymentMethodHandlerDictionary
@{ @{
var storeId = Context.GetImplicitStoreId(); var storeId = Context.GetImplicitStoreId();
var address = Context.GetRouteValue("address").ToString(); var address = Context.GetRouteValue("address").ToString();
ViewData.SetActivePage("Breez", "Create Swapin Refund", "SwapIn"); ViewData.SetActivePage("Breez", "Create Swapin Refund", "SwapIn");
var deriv = Context.GetStoreData().GetDerivationSchemeSettings(BtcPayNetworkProvider, "BTC"); var deriv = Context.GetStoreData().GetDerivationSchemeSettings(PaymentMethodHandlerDictionary, "BTC");
var sdk = BreezService.GetClient(storeId)?.Sdk; var sdk = BreezService.GetClient(storeId)?.Sdk;
var f = sdk.RecommendedFees(); var f = sdk.RecommendedFees();
} }

View File

@@ -1,11 +1,12 @@
@using Breez.Sdk @using Breez.Sdk
@using BTCPayServer @using BTCPayServer
@using BTCPayServer.Abstractions.Extensions
@using BTCPayServer.Models.StoreViewModels @using BTCPayServer.Models.StoreViewModels
@using BTCPayServer.Plugins.Breez @using BTCPayServer.Plugins.Breez
@using BTCPayServer.Security @using BTCPayServer.Security
@using BTCPayServer.Services.Invoices
@using Microsoft.AspNetCore.Mvc.TagHelpers
@inject BreezService BreezService @inject BreezService BreezService
@inject BTCPayNetworkProvider BtcPayNetworkProvider @inject PaymentMethodHandlerDictionary PaymentMethodHandlerDictionary
@{ @{
@@ -29,7 +30,7 @@
if (sdk is null) if (sdk is null)
return; return;
var inProgressSwaps = sdk.InProgressReverseSwaps(); var inProgressSwaps = sdk.InProgressReverseSwaps();
var deriv = Context.GetStoreData().GetDerivationSchemeSettings(BtcPayNetworkProvider, "BTC"); var deriv = Context.GetStoreData().GetDerivationSchemeSettings(PaymentMethodHandlerDictionary, "BTC");
var f = sdk.RecommendedFees(); var f = sdk.RecommendedFees();
var swapOutRec = sdk.FetchReverseSwapFees(new ReverseSwapFeesRequest()); var swapOutRec = sdk.FetchReverseSwapFees(new ReverseSwapFeesRequest());
} }

View File

@@ -2,9 +2,10 @@
@using BTCPayServer.Models.StoreViewModels @using BTCPayServer.Models.StoreViewModels
@using BTCPayServer.Plugins.Breez @using BTCPayServer.Plugins.Breez
@using BTCPayServer.Security @using BTCPayServer.Security
@using BTCPayServer.Services.Invoices
@using Microsoft.AspNetCore.Mvc.TagHelpers @using Microsoft.AspNetCore.Mvc.TagHelpers
@inject BreezService BreezService @inject BreezService BreezService
@inject BTCPayNetworkProvider BtcPayNetworkProvider @inject PaymentMethodHandlerDictionary PaymentMethodHandlerDictionary
@{ @{
Layout = "_Layout"; Layout = "_Layout";
ViewData.SetActivePage("Breez", "Sweep", "Sweep"); ViewData.SetActivePage("Breez", "Sweep", "Sweep");
@@ -24,7 +25,7 @@
var sdk = BreezService.GetClient(storeId)?.Sdk; var sdk = BreezService.GetClient(storeId)?.Sdk;
if (sdk is null) if (sdk is null)
return; return;
var deriv = Context.GetStoreData().GetDerivationSchemeSettings(BtcPayNetworkProvider, "BTC"); var deriv = Context.GetStoreData().GetDerivationSchemeSettings(PaymentMethodHandlerDictionary, "BTC");
var f = sdk.RecommendedFees(); var f = sdk.RecommendedFees();
} }
<datalist id="fees"> <datalist id="fees">

View File

@@ -1,10 +0,0 @@
@{
Layout = "../Shared/_NavLayout.cshtml";
ViewData["NavPartialName"] = "_Nav";
}
<style>
#mainContent > section {
padding: 3rem !important;
}
</style>
@RenderBody()

View File

@@ -1,41 +0,0 @@
@using BTCPayServer.Abstractions.Extensions
@using BTCPayServer.Client
@using BTCPayServer.Models.StoreViewModels
@using BTCPayServer.Plugins.Breez
@using BTCPayServer.Security
@using Microsoft.AspNetCore.Mvc.TagHelpers
@inject BreezService BreezService
@{
var storeId = Model switch
{
string s => s,
StoreDashboardViewModel dashboardModel => dashboardModel.StoreId,
_ => Context.GetImplicitStoreId()
};
var client = BreezService.GetClient(storeId);
var sdk = client?.Sdk;
}
<div class="sticky-header-setup"></div>
<div class="sticky-header mb-l">
<h2 class="mt-1 mb-2 mb-lg-4">Breez / Greenlight</h2>
@if (sdk is not null)
{
<nav id="SectionNav">
<div class="nav">
<a permission="@Policies.CanViewStoreSettings" asp-action="Info" asp-route-storeId="@storeId" class="nav-link @ViewData.IsActivePage("Breez", null, "Info")">Info</a>
<a permission="@Policies.CanViewStoreSettings" asp-action="Payments" asp-route-storeId="@storeId" class="nav-link @ViewData.IsActivePage("Breez", null, "Payments")">Payments</a>
<a permission="@Policies.CanCreateInvoice" asp-action="SwapIn" asp-route-storeId="@storeId" class="nav-link @ViewData.IsActivePage("Breez", null, "SwapIn")">Swap In</a>
<a permission="@Policies.CanModifyStoreSettings" asp-action="SwapOut" asp-route-storeId="@storeId" class="nav-link @ViewData.IsActivePage("Breez", null, "SwapOut")">Swap Out</a>
<a permission="@Policies.CanModifyStoreSettings" asp-action="Configure" asp-route-storeId="@storeId" class="nav-link @ViewData.IsActivePage("Breez", null, "Configure")">Configuration</a>
@if (client.Events.Any())
{
<a permission="@Policies.CanViewStoreSettings" asp-action="Logs" asp-route-storeId="@storeId" class="nav-link @ViewData.IsActivePage("Breez", null, "Logs")">Logs</a>
}
</div>
</nav>
}
</div>

View File

@@ -1,27 +1,67 @@
@using Breez.Sdk
@using BTCPayServer.Abstractions.Contracts @using BTCPayServer.Abstractions.Contracts
@using BTCPayServer.Abstractions.Extensions @using BTCPayServer.Abstractions.Extensions
@using BTCPayServer.Client @using BTCPayServer.Client
@using BTCPayServer.Models.StoreViewModels
@using BTCPayServer.Plugins.Breez
@using BTCPayServer.Security
@using Microsoft.AspNetCore.Mvc.TagHelpers @using Microsoft.AspNetCore.Mvc.TagHelpers
@inject IScopeProvider ScopeProvider @inject IScopeProvider ScopeProvider
@inject BreezService BreezService
@{ @{
var storeId = ScopeProvider.GetCurrentStoreId(); var storeId = Model switch
{
string s => s,
StoreDashboardViewModel dashboardModel => dashboardModel.StoreId,
_ => Context.GetImplicitStoreId()
};
var active = @ViewData.IsActivePage("Breez");
var client = string.IsNullOrEmpty(active) ? null : BreezService.GetClient(storeId);
var sdk = client?.Sdk;
} }
@if (!string.IsNullOrEmpty(storeId)) @if (!string.IsNullOrEmpty(storeId))
{ {
<li class="nav-item"> <li class="nav-item">
<a permission="@Policies.CanViewStoreSettings" asp-controller="Breez" asp-action="Index" asp-route-storeId="@storeId" class="nav-link @ViewData.IsActivePage("Breez")" id="Nav-Breez"> <a permission="@Policies.CanViewStoreSettings" asp-controller="Breez" asp-action="Index" asp-route-storeId="@storeId" class="nav-link @active" id="Nav-Breez">
<svg style="width: 15px; margin-left: 3px; margin-right: 7px;" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <svg style="width: 15px; margin-left: 3px; margin-right: 7px;" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="favicon-64-2" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> <g id="favicon-64-2" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Group" transform="translate(3.000000, 2.000000)" fill-rule="nonzero"> <g id="Group" transform="translate(3.000000, 2.000000)" fill-rule="nonzero">
<path d="M35.4723926,39.8214286 C35.4723926,37.9541367 33.9340883,36.4285714 32.0236136,36.4285714 L10.3260796,36.4285714 C8.42801059,36.4285714 6.87730061,37.9419322 6.87730061,39.8214286 C6.87730061,41.6887205 8.41560491,43.2142857 10.3260796,43.2142857 L32.0236136,43.2142857 C33.9216827,43.2020812 35.4723926,41.6887205 35.4723926,39.8214286 Z" id="Shape" fill="currentColor"></path> <path d="M35.4723926,39.8214286 C35.4723926,37.9541367 33.9340883,36.4285714 32.0236136,36.4285714 L10.3260796,36.4285714 C8.42801059,36.4285714 6.87730061,37.9419322 6.87730061,39.8214286 C6.87730061,41.6887205 8.41560491,43.2142857 10.3260796,43.2142857 L32.0236136,43.2142857 C33.9216827,43.2020812 35.4723926,41.6887205 35.4723926,39.8214286 Z" id="Shape" fill="currentColor"></path>
<path d="M49.7395693,25.9068425 C51.3370507,23.7180544 52.2855553,21.0346249 52.2855553,18.1162407 C52.2855553,10.7460841 46.257559,4.77328937 38.819286,4.77328937 L36.9721981,4.77328937 L36.9721981,3.32646331 C36.9721981,1.48392415 35.462079,0 33.614991,0 C31.7554228,0 30.257784,1.49629019 30.257784,3.32646331 L30.257784,4.77328937 L26.8381753,4.77328937 L26.8381753,4.7856554 L24.6790793,4.7856554 C22.8819126,4.7856554 21.4341951,6.23248145 21.4341951,8.0008244 C21.4341951,9.78153339 22.894393,11.2159934 24.6790793,11.2159934 L26.8381753,11.2159934 L26.8381753,11.2283594 L38.7693647,11.2283594 C42.5009815,11.2283594 45.5212199,14.2209398 45.5212199,17.9183842 L45.5212199,17.9307502 C45.5212199,21.5292663 42.6507454,24.5342127 39.0439319,24.6084089 C38.3824747,24.6207749 36.3856229,24.6084089 36.3856229,24.6084089 C36.3731426,24.6084089 36.3481819,24.6084089 36.3357016,24.6084089 L36.310741,24.6084089 C34.3263695,24.657873 32.7413684,26.3520198 32.9535339,28.3800495 C33.1407387,30.1483924 34.7382202,31.4468261 36.5353868,31.4468261 L43.3496435,31.4468261 C44.0485417,31.4468261 44.7599201,31.4962902 45.4338576,31.6694147 C49.0905925,32.6215993 51.7988227,35.9109646 51.7988227,39.8433636 C51.7988227,44.5053586 47.9798437,48.2893652 43.2747616,48.2893652 L22.7071881,48.2893652 L18.8382878,48.2893652 C17.0161605,48.2893652 15.3937184,49.6125309 15.2189939,51.4056059 C15.0193087,53.4583677 16.6542311,55.1896125 18.6885239,55.1896125 L30.257784,55.1896125 L30.257784,56.6735367 C30.257784,58.5160758 31.7679031,60 33.614991,60 C35.4745593,60 36.9721981,58.5037098 36.9721981,56.6735367 L36.9721981,55.1896125 L39.8925939,55.1896125 C39.8925939,55.1896125 39.8925939,55.1896125 39.9050742,55.1896125 L43.5742894,55.1896125 C52.0983505,55.1896125 58.9999695,48.3388293 58.9999695,39.9051937 C59.0124498,33.6479802 55.1934708,28.2563891 49.7395693,25.9068425 Z" id="Shape" fill="currentColor"></path> <path d="M49.7395693,25.9068425 C51.3370507,23.7180544 52.2855553,21.0346249 52.2855553,18.1162407 C52.2855553,10.7460841 46.257559,4.77328937 38.819286,4.77328937 L36.9721981,4.77328937 L36.9721981,3.32646331 C36.9721981,1.48392415 35.462079,0 33.614991,0 C31.7554228,0 30.257784,1.49629019 30.257784,3.32646331 L30.257784,4.77328937 L26.8381753,4.77328937 L26.8381753,4.7856554 L24.6790793,4.7856554 C22.8819126,4.7856554 21.4341951,6.23248145 21.4341951,8.0008244 C21.4341951,9.78153339 22.894393,11.2159934 24.6790793,11.2159934 L26.8381753,11.2159934 L26.8381753,11.2283594 L38.7693647,11.2283594 C42.5009815,11.2283594 45.5212199,14.2209398 45.5212199,17.9183842 L45.5212199,17.9307502 C45.5212199,21.5292663 42.6507454,24.5342127 39.0439319,24.6084089 C38.3824747,24.6207749 36.3856229,24.6084089 36.3856229,24.6084089 C36.3731426,24.6084089 36.3481819,24.6084089 36.3357016,24.6084089 L36.310741,24.6084089 C34.3263695,24.657873 32.7413684,26.3520198 32.9535339,28.3800495 C33.1407387,30.1483924 34.7382202,31.4468261 36.5353868,31.4468261 L43.3496435,31.4468261 C44.0485417,31.4468261 44.7599201,31.4962902 45.4338576,31.6694147 C49.0905925,32.6215993 51.7988227,35.9109646 51.7988227,39.8433636 C51.7988227,44.5053586 47.9798437,48.2893652 43.2747616,48.2893652 L22.7071881,48.2893652 L18.8382878,48.2893652 C17.0161605,48.2893652 15.3937184,49.6125309 15.2189939,51.4056059 C15.0193087,53.4583677 16.6542311,55.1896125 18.6885239,55.1896125 L30.257784,55.1896125 L30.257784,56.6735367 C30.257784,58.5160758 31.7679031,60 33.614991,60 C35.4745593,60 36.9721981,58.5037098 36.9721981,56.6735367 L36.9721981,55.1896125 L39.8925939,55.1896125 C39.8925939,55.1896125 39.8925939,55.1896125 39.9050742,55.1896125 L43.5742894,55.1896125 C52.0983505,55.1896125 58.9999695,48.3388293 58.9999695,39.9051937 C59.0124498,33.6479802 55.1934708,28.2563891 49.7395693,25.9068425 Z" id="Shape" fill="currentColor"></path>
<path d="M22.4417178,28.0357143 C22.4417178,26.1684224 20.8948734,24.6428571 18.9737925,24.6428571 L3.46792526,24.6428571 C1.55931891,24.6428571 0,26.1562179 0,28.0357143 C0,29.9030062 1.54684436,31.4285714 3.46792526,31.4285714 L18.9737925,31.4285714 C20.8948734,31.4285714 22.4417178,29.9030062 22.4417178,28.0357143 Z" id="Shape" fill="currentColor"></path> <path d="M22.4417178,28.0357143 C22.4417178,26.1684224 20.8948734,24.6428571 18.9737925,24.6428571 L3.46792526,24.6428571 C1.55931891,24.6428571 0,26.1562179 0,28.0357143 C0,29.9030062 1.54684436,31.4285714 3.46792526,31.4285714 L18.9737925,31.4285714 C20.8948734,31.4285714 22.4417178,29.9030062 22.4417178,28.0357143 Z" id="Shape" fill="currentColor"></path>
<path d="M15.7746737,21.4285714 L31.280541,21.4285714 C33.1891473,21.4285714 34.7484663,19.9152107 34.7484663,18.0357143 C34.7484663,16.1684224 33.2016219,14.6428571 31.280541,14.6428571 L15.7746737,14.6428571 C13.8660674,14.6428571 12.3067485,16.1562179 12.3067485,18.0357143 C12.3067485,19.9030062 13.8535928,21.4285714 15.7746737,21.4285714 Z" id="Shape" fill="currentColor"></path> <path d="M15.7746737,21.4285714 L31.280541,21.4285714 C33.1891473,21.4285714 34.7484663,19.9152107 34.7484663,18.0357143 C34.7484663,16.1684224 33.2016219,14.6428571 31.280541,14.6428571 L15.7746737,14.6428571 C13.8660674,14.6428571 12.3067485,16.1562179 12.3067485,18.0357143 C12.3067485,19.9030062 13.8535928,21.4285714 15.7746737,21.4285714 Z" id="Shape" fill="currentColor"></path>
</g> </g>
</g> </g>
</svg> </svg>
<span>Breez</span> <span>Breez</span>
</a> </a>
</li> </li>
}
@if (sdk is not null)
{
<li class="nav-item nav-item-sub">
<a permission="@Policies.CanViewStoreSettings" asp-action="Info" asp-route-storeId="@storeId" class="nav-link @ViewData.IsActivePage("Breez", null, "Info")">Info</a>
</li>
<li class="nav-item nav-item-sub">
<a permission="@Policies.CanViewStoreSettings" asp-action="Payments" asp-route-storeId="@storeId" class="nav-link @ViewData.IsActivePage("Breez", null, "Payments")">Payments</a>
</li>
<li class="nav-item nav-item-sub">
<a permission="@Policies.CanCreateInvoice" asp-action="SwapIn" asp-route-storeId="@storeId" class="nav-link @ViewData.IsActivePage("Breez", null, "SwapIn")">Swap In</a>
</li>
<li class="nav-item nav-item-sub">
<a permission="@Policies.CanModifyStoreSettings" asp-action="SwapOut" asp-route-storeId="@storeId" class="nav-link @ViewData.IsActivePage("Breez", null, "SwapOut")">Swap Out</a>
</li>
<li class="nav-item nav-item-sub">
<a permission="@Policies.CanModifyStoreSettings" asp-action="Configure" asp-route-storeId="@storeId" class="nav-link @ViewData.IsActivePage("Breez", null, "Configure")">Configuration</a>
</li>
@if (client.Events.Any())
{
<li class="nav-item nav-item-sub">
<a permission="@Policies.CanViewStoreSettings" asp-action="Logs" asp-route-storeId="@storeId" class="nav-link @ViewData.IsActivePage("Breez", null, "Logs")">Logs</a>
</li>
}
}
}

View File

@@ -103,7 +103,7 @@
<span class="text-secondary fw-semibold currency">BTC receivable</span> <span class="text-secondary fw-semibold currency">BTC receivable</span>
</div> </div>
<div class="balance d-flex align-items-baseline gap-1"> <div class="balance d-flex align-items-baseline gap-1">
<h3 class="d-inline-block me-1" data-balance="@LightMoney.MilliSatoshis(nodeState.inboundLiquidityMsats).ToUnit(LightMoneyUnit.BTC)" data-sensitive>@LightMoney.MilliSatoshis(nodeState.inboundLiquidityMsats).ToUnit(LightMoneyUnit.BTC)</h3> <h3 class="d-inline-block me-1" data-balance="@LightMoney.MilliSatoshis(nodeState.totalInboundLiquidityMsats).ToUnit(LightMoneyUnit.BTC)" data-sensitive>@LightMoney.MilliSatoshis(nodeState.totalInboundLiquidityMsats).ToUnit(LightMoneyUnit.BTC)</h3>
<span class="text-secondary fw-semibold currency">BTC inbound liquidity</span> <span class="text-secondary fw-semibold currency">BTC inbound liquidity</span>
</div> </div>
<div class="balance d-flex align-items-baseline gap-1"> <div class="balance d-flex align-items-baseline gap-1">

View File

@@ -9,7 +9,7 @@
<PropertyGroup> <PropertyGroup>
<Product>Bringin</Product> <Product>Bringin</Product>
<Description>Euro Offramp</Description> <Description>Euro Offramp</Description>
<Version>1.0.1</Version> <Version>1.0.2</Version>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup> </PropertyGroup>
<!-- Plugin development properties --> <!-- Plugin development properties -->
@@ -41,6 +41,6 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="AsyncKeyedLock" Version="6.3.4" /> <PackageReference Include="AsyncKeyedLock" Version="7.0.1" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -11,7 +11,10 @@ public class BringinPlugin : BaseBTCPayServerPlugin
{ {
public override IBTCPayServerPlugin.PluginDependency[] Dependencies { get; } = public override IBTCPayServerPlugin.PluginDependency[] Dependencies { get; } =
{ {
new() {Identifier = nameof(BTCPayServer), Condition = ">=1.12.0"} new()
{
Identifier = nameof(BTCPayServer), Condition = ">=2.0.0"
}
}; };
public override void Execute(IServiceCollection applicationBuilder) public override void Execute(IServiceCollection applicationBuilder)

View File

@@ -17,6 +17,7 @@ using BTCPayServer.Events;
using BTCPayServer.HostedServices; using BTCPayServer.HostedServices;
using BTCPayServer.Lightning; using BTCPayServer.Lightning;
using BTCPayServer.Payments; using BTCPayServer.Payments;
using BTCPayServer.Payouts;
using BTCPayServer.Services; using BTCPayServer.Services;
using BTCPayServer.Services.Stores; using BTCPayServer.Services.Stores;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@@ -139,13 +140,15 @@ public class BringinService : EventHostedServiceBase
invoiceEvent when bringinStoreSettings.Enabled: invoiceEvent when bringinStoreSettings.Enabled:
var pmPayments = invoiceEvent.Invoice.GetPayments("BTC", true) var pmPayments = invoiceEvent.Invoice.GetPayments("BTC", true)
.GroupBy(payment => payment.GetPaymentMethodId()); .GroupBy(payment => payment.PaymentMethodId);
var update = false; var update = false;
foreach (var pmPayment in pmPayments) foreach (var pmPayment in pmPayments)
{ {
var methodId = pmPayment.Key; var methodId = pmPayment.Key;
if (methodId.PaymentType == PaymentTypes.LNURLPay) if (methodId == PaymentTypes.LNURL.GetPaymentMethodId("BTC"))
methodId = new PaymentMethodId(methodId.CryptoCode, PaymentTypes.LightningLike); {
methodId = PaymentTypes.LN.GetPaymentMethodId("BTC");
}
if (!bringinStoreSettings.MethodSettings.TryGetValue(methodId.ToString(), if (!bringinStoreSettings.MethodSettings.TryGetValue(methodId.ToString(),
out var methodSettings)) out var methodSettings))
{ {
@@ -153,7 +156,7 @@ public class BringinService : EventHostedServiceBase
} }
methodSettings.CurrentBalance += methodSettings.CurrentBalance +=
pmPayment.Sum(payment => payment.GetCryptoPaymentData().GetValue()); pmPayment.Sum(payment => payment.Value);
update = true; update = true;
} }
@@ -190,14 +193,14 @@ public class BringinService : EventHostedServiceBase
foreach (var methodSetting in bringinStoreSetting.MethodSettings) foreach (var methodSetting in bringinStoreSetting.MethodSettings)
{ {
var pmi = PaymentMethodId.TryParse(methodSetting.Key); var pmi = PayoutMethodId.TryParse(methodSetting.Key);
if (pmi is null) if (pmi is null)
{ {
continue; continue;
} }
var isOnChain = PayoutTypes.CHAIN.GetPayoutMethodId("BTC") == pmi;
// if there is a pending payout, try and cancel it if this is onchain as we want to save needless tx fees // if there is a pending payout, try and cancel it if this is onchain as we want to save needless tx fees
if (methodSetting.Value.PendingPayouts.Count > 0 && pmi.PaymentType == BitcoinPaymentType.Instance) if (methodSetting.Value.PendingPayouts.Count > 0 && isOnChain)
{ {
var cancelResult = await _pullPaymentHostedService.Cancel( var cancelResult = await _pullPaymentHostedService.Cancel(
new PullPaymentHostedService.CancelRequest(methodSetting.Value.PendingPayouts.ToArray(), new PullPaymentHostedService.CancelRequest(methodSetting.Value.PendingPayouts.ToArray(),
@@ -252,16 +255,16 @@ public class BringinService : EventHostedServiceBase
return result; return result;
} }
public async Task<string?> CreateOrder(string storeId, PaymentMethodId paymentMethodId, Money amountBtc, bool payout) public async Task<string?> CreateOrder(string storeId, PayoutMethodId paymentMethodId, Money amountBtc, bool payout)
{ {
if (SupportedMethods.All(supportedMethod => supportedMethod.PaymentMethod != paymentMethodId)) if (SupportedMethods.All(supportedMethod => supportedMethod.PayoutMethod != paymentMethodId))
{ {
throw new NotSupportedException($"{paymentMethodId.ToPrettyString()} Payment method not supported"); throw new NotSupportedException($"{paymentMethodId} Payment method not supported");
} }
var settings = _settings[storeId]; var settings = _settings[storeId];
var bringinClient = settings.CreateClient(_httpClientFactory, _btcPayNetworkProvider.GetNetwork<BTCPayNetwork>(paymentMethodId.CryptoCode).NBitcoinNetwork); var bringinClient = settings.CreateClient(_httpClientFactory, _btcPayNetworkProvider.GetNetwork<BTCPayNetwork>("BTC").NBitcoinNetwork);
var host = await Dns.GetHostEntryAsync(Dns.GetHostName(), CancellationToken.None); var host = await Dns.GetHostEntryAsync(Dns.GetHostName(), CancellationToken.None);
@@ -270,7 +273,7 @@ public class BringinService : EventHostedServiceBase
var supportedMethod = SupportedMethods.First(supportedMethod => supportedMethod.PaymentMethod == paymentMethodId); var supportedMethod = SupportedMethods.First(supportedMethod => supportedMethod.PayoutMethod == paymentMethodId);
//check if amount is enough //check if amount is enough
if (supportedMethod.FiatMinimumAmount > 0) if (supportedMethod.FiatMinimumAmount > 0)
{ {
@@ -297,13 +300,13 @@ public class BringinService : EventHostedServiceBase
{ {
return order.Invoice?? order.DepositAddress; return order.Invoice?? order.DepositAddress;
} }
var network = _btcPayNetworkProvider.GetNetwork<BTCPayNetwork>(paymentMethodId.CryptoCode); var network = _btcPayNetworkProvider.GetNetwork<BTCPayNetwork>("BTC");
var destination = !string.IsNullOrEmpty(order.Invoice)? (IClaimDestination) new BoltInvoiceClaimDestination(order.Invoice, BOLT11PaymentRequest.Parse(order.Invoice, network.NBitcoinNetwork)): var destination = !string.IsNullOrEmpty(order.Invoice)? (IClaimDestination) new BoltInvoiceClaimDestination(order.Invoice, BOLT11PaymentRequest.Parse(order.Invoice, network.NBitcoinNetwork)):
new AddressClaimDestination(BitcoinAddress.Create(order.DepositAddress, network.NBitcoinNetwork)); new AddressClaimDestination(BitcoinAddress.Create(order.DepositAddress, network.NBitcoinNetwork));
var claim = await _pullPaymentHostedService.Claim(new ClaimRequest() var claim = await _pullPaymentHostedService.Claim(new ClaimRequest()
{ {
PaymentMethodId = paymentMethodId, PayoutMethodId = paymentMethodId,
StoreId = storeId, StoreId = storeId,
Destination = destination, Destination = destination,
Value = orderMoney.ToUnit(MoneyUnit.BTC), Value = orderMoney.ToUnit(MoneyUnit.BTC),
@@ -363,17 +366,17 @@ public class BringinService : EventHostedServiceBase
{ {
case PayoutState.Completed: case PayoutState.Completed:
// remove from pending payouts list in a setting // remove from pending payouts list in a setting
return _settings[payout.StoreDataId].MethodSettings[payout.GetPaymentMethodId().ToString()] return _settings[payout.StoreDataId].MethodSettings[payout.GetPayoutMethodId().ToString()]
.PendingPayouts.Remove(payout.Id); .PendingPayouts.Remove(payout.Id);
case PayoutState.Cancelled: case PayoutState.Cancelled:
// remove from pending payouts list in a setting and add to a balance // remove from pending payouts list in a setting and add to a balance
var result = _settings[payout.StoreDataId].MethodSettings[payout.GetPaymentMethodId().ToString()] var result = _settings[payout.StoreDataId].MethodSettings[payout.GetPayoutMethodId().ToString()]
.PendingPayouts.Remove(payout.Id); .PendingPayouts.Remove(payout.Id);
var pmi = payout.GetPaymentMethodId(); var pmi = payout.GetPayoutMethodId();
if (_settings[payout.StoreDataId].MethodSettings if (_settings[payout.StoreDataId].MethodSettings
.TryGetValue(pmi.ToString(), out var methodSettings)) .TryGetValue(pmi.ToString(), out var methodSettings))
{ {
methodSettings.CurrentBalance += payout.GetBlob(_btcPayNetworkJsonSerializerSettings).Amount; methodSettings.CurrentBalance += payout.Amount ?? payout.OriginalAmount;
result = true; result = true;
} }
@@ -389,12 +392,12 @@ public class BringinService : EventHostedServiceBase
return _settings.TryGetValue(storeId, out var bringinStoreSettings) ? bringinStoreSettings : null; return _settings.TryGetValue(storeId, out var bringinStoreSettings) ? bringinStoreSettings : null;
} }
public record SupportedMethodOptions(PaymentMethodId PaymentMethod, bool FiatMinimum, decimal FiatMinimumAmount, string bringinMethod); public record SupportedMethodOptions(PayoutMethodId PayoutMethod, bool FiatMinimum, decimal FiatMinimumAmount, string bringinMethod);
public static readonly SupportedMethodOptions[] SupportedMethods = new[] public static readonly SupportedMethodOptions[] SupportedMethods = new[]
{ {
new SupportedMethodOptions(new PaymentMethodId("BTC", LightningPaymentType.Instance), true, 22, "LIGHTNING"), new SupportedMethodOptions(PayoutTypes.LN.GetPayoutMethodId("BTC"), true, 22, "LIGHTNING"),
new SupportedMethodOptions(new PaymentMethodId("BTC", BitcoinPaymentType.Instance), true, 22, "ON_CHAIN"), new SupportedMethodOptions(PayoutTypes.CHAIN.GetPayoutMethodId("BTC"), true, 22, "ON_CHAIN"),
}; };
private ConcurrentDictionary<string, (IDisposable, BringinStoreSettings, DateTimeOffset Expiry)> _editModes = new(); private ConcurrentDictionary<string, (IDisposable, BringinStoreSettings, DateTimeOffset Expiry)> _editModes = new();
@@ -412,9 +415,9 @@ public class BringinService : EventHostedServiceBase
// add or remove any missing methods in result // add or remove any missing methods in result
foreach (var supportedMethod in SupportedMethods) foreach (var supportedMethod in SupportedMethods)
{ {
if (!result.MethodSettings.ContainsKey(supportedMethod.PaymentMethod.ToString())) if (!result.MethodSettings.ContainsKey(supportedMethod.PayoutMethod.ToString()))
{ {
result.MethodSettings.Add(supportedMethod.PaymentMethod.ToString(), result.MethodSettings.Add(supportedMethod.PayoutMethod.ToString(),
new BringinStoreSettings.PaymentMethodSettings() new BringinStoreSettings.PaymentMethodSettings()
{ {
FiatThreshold = supportedMethod.FiatMinimum, FiatThreshold = supportedMethod.FiatMinimum,
@@ -424,7 +427,7 @@ public class BringinService : EventHostedServiceBase
} }
result.MethodSettings = result.MethodSettings.Where(pair => result.MethodSettings = result.MethodSettings.Where(pair =>
SupportedMethods.Any(supportedMethod => supportedMethod.PaymentMethod.ToString() == pair.Key)) SupportedMethods.Any(supportedMethod => supportedMethod.PayoutMethod.ToString() == pair.Key))
.ToDictionary(pair => pair.Key, pair => pair.Value); .ToDictionary(pair => pair.Key, pair => pair.Value);
isNew = true; isNew = true;
return (storeLock, result, DateTimeOffset.Now.AddMinutes(5)); return (storeLock, result, DateTimeOffset.Now.AddMinutes(5));

View File

@@ -4,7 +4,9 @@
@using BTCPayServer.Data @using BTCPayServer.Data
@using BTCPayServer.Payments @using BTCPayServer.Payments
@using BTCPayServer.PayoutProcessors @using BTCPayServer.PayoutProcessors
@using BTCPayServer.Payouts
@using BTCPayServer.Services @using BTCPayServer.Services
@using BTCPayServer.Services.Invoices
@using BTCPayServer.Services.Stores @using BTCPayServer.Services.Stores
@using Microsoft.AspNetCore.Http @using Microsoft.AspNetCore.Http
@using Microsoft.AspNetCore.Routing @using Microsoft.AspNetCore.Routing
@@ -25,6 +27,7 @@
[Inject] private IHttpClientFactory HttpClientFactory { get; set; } [Inject] private IHttpClientFactory HttpClientFactory { get; set; }
[Inject] private PayoutProcessorService PayoutProcessorService { get; set; } [Inject] private PayoutProcessorService PayoutProcessorService { get; set; }
[Inject] private IAuthorizationService AuthorizationService { get; set; } [Inject] private IAuthorizationService AuthorizationService { get; set; }
[Inject] private PayoutMethodHandlerDictionary PayoutMethodHandlerDictionary { get; set; }
[Parameter] public string StoreId { get; set; } [Parameter] public string StoreId { get; set; }
private decimal? LastFiatBalance { get; set; } private decimal? LastFiatBalance { get; set; }
private DateTimeOffset? LastDataFetch { get; set; } private DateTimeOffset? LastDataFetch { get; set; }
@@ -79,12 +82,13 @@
PmiLink = $"A payout processor has not been configured for this payment method. Payouts generated by Bringin will not be automatically handled. <a href=\"{LinkGenerator.GetUriByAction(HttpContextAccessor.HttpContext, "ConfigureStorePayoutProcessors", "UIPayoutProcessors", new {StoreId})}\">Configure now</a>"; PmiLink = $"A payout processor has not been configured for this payment method. Payouts generated by Bringin will not be automatically handled. <a href=\"{LinkGenerator.GetUriByAction(HttpContextAccessor.HttpContext, "ConfigureStorePayoutProcessors", "UIPayoutProcessors", new {StoreId})}\">Configure now</a>";
_callbackLink = LinkGenerator.GetUriByAction(HttpContextAccessor.HttpContext, "Callback", "Bringin", new {StoreId}); _callbackLink = LinkGenerator.GetUriByAction(HttpContextAccessor.HttpContext, "Callback", "Bringin", new {StoreId});
_settings = BringinService.IsInEditMode(StoreId) ? await BringinService.Update(StoreId) : await BringinService.Get(StoreId); _settings = BringinService.IsInEditMode(StoreId) ? await BringinService.Update(StoreId) : await BringinService.Get(StoreId);
_pms = (await StoreRepository.FindStore(StoreId)).GetSupportedPaymentMethods(BTCPayNetworkProvider).Select(method => method.PaymentId).Where(id => id.CryptoCode == "BTC").ToArray(); var store = await StoreRepository.FindStore(StoreId);
_pms = PayoutMethodHandlerDictionary.GetSupportedPayoutMethods(store);
_pps = (await PayoutProcessorService.GetProcessors(new PayoutProcessorService.PayoutProcessorQuery() _pps = (await PayoutProcessorService.GetProcessors(new PayoutProcessorService.PayoutProcessorQuery()
{ {
Stores = new[] {StoreId}, Stores = new[] {StoreId},
PaymentMethods = _pms.Select(p => p.ToString()).ToArray() PayoutMethods = _pms.Select(p => p).ToArray()
})).Select(data => PaymentMethodId.TryParse(data.PaymentMethod)).Where(id => id is not null).ToArray(); })).Select(data => PayoutMethodId.TryParse(data.PayoutMethodId)).Where(id => id is not null).ToArray();
EditMode = BringinService.IsInEditMode(StoreId); EditMode = BringinService.IsInEditMode(StoreId);
IsLoaded = true; IsLoaded = true;
_ = FetchBalanceAndRate(); _ = FetchBalanceAndRate();
@@ -148,8 +152,8 @@
} }
private bool _saving; private bool _saving;
private PaymentMethodId[] _pms; private HashSet<PayoutMethodId> _pms;
private PaymentMethodId[] _pps; private PayoutMethodId[] _pps;
private bool _apiKeyError; private bool _apiKeyError;
private async Task Save() private async Task Save()
@@ -269,7 +273,7 @@
_saving = true; _saving = true;
await InvokeAsync(StateHasChanged); await InvokeAsync(StateHasChanged);
var pm = PaymentMethodId.TryParse(ManualOrderPaymentMethod); var pm = PayoutMethodId.TryParse(ManualOrderPaymentMethod);
if (pm is null) if (pm is null)
{ {
SaveError = "Invalid payment method"; SaveError = "Invalid payment method";
@@ -391,8 +395,8 @@
} }
else if (_manualOrder) else if (_manualOrder)
{ {
var items = new List<PaymentMethodId>(); var items = new List<PayoutMethodId>();
items.AddRange(BringinService.SupportedMethods.Where(s => _pms.Contains(s.PaymentMethod)).Select(s => s.PaymentMethod)); items.AddRange(BringinService.SupportedMethods.Where(s => _pms.Contains(s.PayoutMethod)).Select(s => s.PayoutMethod));
<div class="row"> <div class="row">
@@ -422,7 +426,7 @@
<option value="">Select a payment method</option> <option value="">Select a payment method</option>
@foreach (var opt in items) @foreach (var opt in items)
{ {
<option value="@opt.ToString()">@opt.ToPrettyString()</option> <option value="@opt.ToString()">@opt</option>
} }
</select> </select>
</div> </div>
@@ -472,7 +476,7 @@
@foreach (var method in _settings.MethodSettings) @foreach (var method in _settings.MethodSettings)
{ {
var pmi = PaymentMethodId.TryParse(method.Key); var pmi = PayoutMethodId.TryParse(method.Key);
if (pmi is null) if (pmi is null)
continue; continue;
if (!_pms.Contains(pmi)) if (!_pms.Contains(pmi))
@@ -480,7 +484,7 @@
<hr class=""/> <hr class=""/>
<div class="store-number"> <div class="store-number">
<header> <header>
<h6>@pmi.ToPrettyString()</h6> <h6>@pmi</h6>
</header> </header>
@if (LastFiatRate is null || !method.Value.FiatThreshold) @if (LastFiatRate is null || !method.Value.FiatThreshold)
@@ -504,7 +508,7 @@
<span class="text-secondary"> (@DisplayFormatter.Currency(balanceInFiat.Value, "EUR", DisplayFormatter.CurrencyFormat.Code)) pending to forward once @DisplayFormatter.Currency(thresholdinBtc.Value, "BTC", DisplayFormatter.CurrencyFormat.Code) (@DisplayFormatter.Currency(method.Value.Threshold, "EUR")) is reached.</span> <span class="text-secondary"> (@DisplayFormatter.Currency(balanceInFiat.Value, "EUR", DisplayFormatter.CurrencyFormat.Code)) pending to forward once @DisplayFormatter.Currency(thresholdinBtc.Value, "BTC", DisplayFormatter.CurrencyFormat.Code) (@DisplayFormatter.Currency(method.Value.Threshold, "EUR")) is reached.</span>
@if (method.Value.CurrentBalance > 0) @if (method.Value.CurrentBalance > 0)
{ {
<button class="btn btn-link" @onclick="() => ResetBalance(pmi)" disabled="@_saving">Reset balance</button> <button class="btn btn-link" @onclick="() => ResetBalance(PaymentMethodId.Parse(pmi.ToString()))" disabled="@_saving">Reset balance</button>
//Clear balance //Clear balance
} }
</div> </div>
@@ -579,9 +583,9 @@
var pmId = PaymentMethodId.TryParse(method.Key); var pmId = PaymentMethodId.TryParse(method.Key);
if (pmId is null) if (pmId is null)
continue; continue;
var supportedMethod = BringinService.SupportedMethods.FirstOrDefault(s => s.PaymentMethod.ToString() == method.Key); var supportedMethod = BringinService.SupportedMethods.FirstOrDefault(s => s.PayoutMethod.ToString() == method.Key);
<div class="col-xxl-constrain col-12 @(_settings.MethodSettings.Count > 1 ? $"col-xl-6 {(i == 0 ? "border-end" : "")}" : "")"> <div class="col-xxl-constrain col-12 @(_settings.MethodSettings.Count > 1 ? $"col-xl-6 {(i == 0 ? "border-end" : "")}" : "")">
<h5 class=" border-bottom-0 text-muted mb-4">@pmId.ToPrettyString()</h5> <h5 class=" border-bottom-0 text-muted mb-4">@pmId</h5>
<div class="card-body"> <div class="card-body">
<div class="form-group"> <div class="form-group">
<label class="form-label">Percentage</label> <label class="form-label">Percentage</label>
@@ -644,15 +648,16 @@
<tr> <tr>
<td>@tx.CreatedAt.ToTimeAgo()</td> <td>@tx.CreatedAt.ToTimeAgo()</td>
<td>@tx.SubType.ToHumanReadable()</td> <td>@tx.SubType.ToHumanReadable()</td>
<td> <td>@tx.Status.ToHumanReadable()</td>
@tx.Status.ToHumanReadable()
</td>
<td class="amount-col"> <td class="amount-col">
<span data-sensitive>@(tx.SourceCurrency == "BTC" ? Money.Satoshis(tx.SourceAmount).ToDecimal(MoneyUnit.BTC): tx.SourceAmount)@tx.SourceCurrency -> @tx.DestinationAmount @tx.DestinationCurrency </span> <span data-sensitive>
@(tx.SourceCurrency == "BTC" ? Money.Satoshis(tx.SourceAmount).ToDecimal(MoneyUnit.BTC) : tx.SourceAmount) @tx.SourceCurrency
-> @tx.DestinationAmount @tx.DestinationCurrency
</span>
</td> </td>
</tr> </tr>
} }
</tbody> </tbody>s
</table> </table>
</div> </div>
} }

View File

@@ -9,7 +9,7 @@
<PropertyGroup> <PropertyGroup>
<Product>Data Erasure</Product> <Product>Data Erasure</Product>
<Description>Allows you to erase user data from invoices after a period of time.</Description> <Description>Allows you to erase user data from invoices after a period of time.</Description>
<Version>1.0.2</Version> <Version>1.0.3</Version>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup> </PropertyGroup>
<!-- Plugin development properties --> <!-- Plugin development properties -->

View File

@@ -12,6 +12,7 @@ namespace BTCPayServer.Plugins.DataErasure
public class DataErasureController : Controller public class DataErasureController : Controller
{ {
private readonly DataErasureService _dataErasureService; private readonly DataErasureService _dataErasureService;
public DataErasureController(DataErasureService dataErasureService) public DataErasureController(DataErasureService dataErasureService)
{ {
_dataErasureService = dataErasureService; _dataErasureService = dataErasureService;
@@ -31,10 +32,11 @@ namespace BTCPayServer.Plugins.DataErasure
{ {
if (_dataErasureService.IsRunning) if (_dataErasureService.IsRunning)
{ {
TempData["ErrorMessage"] = "Data erasure is currently running and cannot be changed. Please try again later."; TempData["ErrorMessage"] =
"Data erasure is currently running and cannot be changed. Please try again later.";
} }
if (vm.Enabled) if (vm.Enabled)
{ {
if (!ModelState.IsValid) if (!ModelState.IsValid)
@@ -46,7 +48,11 @@ namespace BTCPayServer.Plugins.DataErasure
switch (command) switch (command)
{ {
case "cleardate":
await _dataErasureService.Set(storeId, vm, true);
TempData["SuccessMessage"] = "Data erasure settings modified and date cleared";
return RedirectToAction(nameof(Update), new {storeId});
case "save": case "save":
await _dataErasureService.Set(storeId, vm); await _dataErasureService.Set(storeId, vm);
TempData["SuccessMessage"] = "Data erasure settings modified"; TempData["SuccessMessage"] = "Data erasure settings modified";
@@ -57,4 +63,4 @@ namespace BTCPayServer.Plugins.DataErasure
} }
} }
} }
} }

View File

@@ -9,7 +9,7 @@ namespace BTCPayServer.Plugins.DataErasure
{ {
public override IBTCPayServerPlugin.PluginDependency[] Dependencies { get; } = public override IBTCPayServerPlugin.PluginDependency[] Dependencies { get; } =
{ {
new() { Identifier = nameof(BTCPayServer), Condition = ">=1.12.0" } new() { Identifier = nameof(BTCPayServer), Condition = ">=2.0.0" }
}; };
public override void Execute(IServiceCollection applicationBuilder) public override void Execute(IServiceCollection applicationBuilder)
{ {

View File

@@ -3,6 +3,7 @@ using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using BTCPayServer.Abstractions.Contracts; using BTCPayServer.Abstractions.Contracts;
using BTCPayServer.Data;
using BTCPayServer.Services.Invoices; using BTCPayServer.Services.Invoices;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@@ -14,13 +15,15 @@ namespace BTCPayServer.Plugins.DataErasure
private readonly IStoreRepository _storeRepository; private readonly IStoreRepository _storeRepository;
private readonly ILogger<DataErasureService> _logger; private readonly ILogger<DataErasureService> _logger;
private readonly InvoiceRepository _invoiceRepository; private readonly InvoiceRepository _invoiceRepository;
private readonly ApplicationDbContextFactory _dbContextFactory;
public DataErasureService(IStoreRepository storeRepository, ILogger<DataErasureService> logger, public DataErasureService(IStoreRepository storeRepository, ILogger<DataErasureService> logger,
InvoiceRepository invoiceRepository) InvoiceRepository invoiceRepository, ApplicationDbContextFactory dbContextFactory)
{ {
_storeRepository = storeRepository; _storeRepository = storeRepository;
_logger = logger; _logger = logger;
_invoiceRepository = invoiceRepository; _invoiceRepository = invoiceRepository;
_dbContextFactory = dbContextFactory;
} }
public async Task<DataErasureSettings> Get(string storeId) public async Task<DataErasureSettings> Get(string storeId)
@@ -29,13 +32,16 @@ namespace BTCPayServer.Plugins.DataErasure
nameof(DataErasureSettings)); nameof(DataErasureSettings));
} }
public async Task Set(string storeId, DataErasureSettings settings) public async Task Set(string storeId, DataErasureSettings settings, bool clearDate = false)
{ {
_cts?.Cancel();
await _runningLock.WaitAsync(); await _runningLock.WaitAsync();
var existing = await Get(storeId); var existing = await Get(storeId);
settings.LastRunCutoff = existing?.LastRunCutoff; settings.LastRunCutoff = clearDate? null: existing?.LastRunCutoff;
await SetCore(storeId, settings); await SetCore(storeId, settings);
_runningLock.Release(); _runningLock.Release();
_cts = new CancellationTokenSource();
_ = Run();
} }
private async Task SetCore(string storeId, DataErasureSettings settings) private async Task SetCore(string storeId, DataErasureSettings settings)
@@ -46,11 +52,11 @@ namespace BTCPayServer.Plugins.DataErasure
public bool IsRunning { get; private set; } public bool IsRunning { get; private set; }
private readonly SemaphoreSlim _runningLock = new(1, 1); private readonly SemaphoreSlim _runningLock = new(1, 1);
private async Task Run(CancellationToken cancellationToken) private async Task Run()
{ {
while (!cancellationToken.IsCancellationRequested) while (!_cts.IsCancellationRequested)
{ {
await _runningLock.WaitAsync(cancellationToken); await _runningLock.WaitAsync(_cts.Token);
IsRunning = true; IsRunning = true;
@@ -58,53 +64,69 @@ namespace BTCPayServer.Plugins.DataErasure
await _storeRepository.GetSettingsAsync<DataErasureSettings>(nameof(DataErasureSettings)); await _storeRepository.GetSettingsAsync<DataErasureSettings>(nameof(DataErasureSettings));
foreach (var setting in settings.Where(setting => setting.Value.Enabled)) foreach (var setting in settings.Where(setting => setting.Value.Enabled))
{ {
var skip = 0;
var count = 0; var count = 0;
var cutoffDate = DateTimeOffset.UtcNow.Subtract(TimeSpan.FromDays(setting.Value.DaysToKeep)); var cutoffDate = DateTimeOffset.UtcNow.Subtract(TimeSpan.FromDays(setting.Value.DaysToKeep));
while (true) if (setting.Value.EntirelyEraseInvoice)
{ {
var invoices = await _invoiceRepository.GetInvoices(new InvoiceQuery() await using var db = _dbContextFactory.CreateContext();
{ db.Invoices.RemoveRange(db.Invoices.Where(i => i.StoreDataId == setting.Key && i.Created < cutoffDate && (setting.Value.LastRunCutoff == null || i.Created > setting.Value.LastRunCutoff)));
StartDate = setting.Value.LastRunCutoff, count = await db.SaveChangesAsync(_cts.Token);
EndDate = cutoffDate,
StoreId = new[] {setting.Key},
Skip = skip,
Take = 100
});
foreach (var invoice in invoices)
{
//replace all buyer info with "erased"
if (!string.IsNullOrEmpty(invoice.Metadata.BuyerAddress1))
invoice.Metadata.BuyerAddress1 = "erased";
if (!string.IsNullOrEmpty(invoice.Metadata.BuyerAddress2))
invoice.Metadata.BuyerAddress2 = "erased";
if (!string.IsNullOrEmpty(invoice.Metadata.BuyerCity))
invoice.Metadata.BuyerCity = "erased";
if (!string.IsNullOrEmpty(invoice.Metadata.BuyerCountry))
invoice.Metadata.BuyerCountry = "erased";
if (!string.IsNullOrEmpty(invoice.Metadata.BuyerEmail))
invoice.Metadata.BuyerEmail = "erased";
if (!string.IsNullOrEmpty(invoice.Metadata.BuyerName))
invoice.Metadata.BuyerName = "erased";
if (!string.IsNullOrEmpty(invoice.Metadata.BuyerPhone))
invoice.Metadata.BuyerPhone = "erased";
if (!string.IsNullOrEmpty(invoice.Metadata.BuyerState))
invoice.Metadata.BuyerState = "erased";
if (!string.IsNullOrEmpty(invoice.Metadata.BuyerZip))
invoice.Metadata.BuyerZip = "erased";
await _invoiceRepository.UpdateInvoiceMetadata(invoice.Id, invoice.StoreId,
invoice.Metadata.ToJObject());
count++;
}
if (invoices.Length < 100)
{
break;
}
skip += 100;
} }
if(count > 0) else
{
var skip = 0;
while (true)
{
var invoices = await _invoiceRepository.GetInvoices(new InvoiceQuery()
{
StartDate = setting.Value.LastRunCutoff,
EndDate = cutoffDate,
StoreId = new[] {setting.Key},
Skip = skip,
Take = 100
}, _cts.Token);
foreach (var invoice in invoices)
{
//replace all buyer info with "erased"
if (!string.IsNullOrEmpty(invoice.Metadata.BuyerAddress1))
invoice.Metadata.BuyerAddress1 = "erased";
if (!string.IsNullOrEmpty(invoice.Metadata.BuyerAddress2))
invoice.Metadata.BuyerAddress2 = "erased";
if (!string.IsNullOrEmpty(invoice.Metadata.BuyerCity))
invoice.Metadata.BuyerCity = "erased";
if (!string.IsNullOrEmpty(invoice.Metadata.BuyerCountry))
invoice.Metadata.BuyerCountry = "erased";
if (!string.IsNullOrEmpty(invoice.Metadata.BuyerEmail))
invoice.Metadata.BuyerEmail = "erased";
if (!string.IsNullOrEmpty(invoice.Metadata.BuyerName))
invoice.Metadata.BuyerName = "erased";
if (!string.IsNullOrEmpty(invoice.Metadata.BuyerPhone))
invoice.Metadata.BuyerPhone = "erased";
if (!string.IsNullOrEmpty(invoice.Metadata.BuyerState))
invoice.Metadata.BuyerState = "erased";
if (!string.IsNullOrEmpty(invoice.Metadata.BuyerZip))
invoice.Metadata.BuyerZip = "erased";
await _invoiceRepository.UpdateInvoiceMetadata(invoice.Id, invoice.StoreId,
invoice.Metadata.ToJObject());
count++;
}
if (invoices.Length < 100)
{
break;
}
skip += 100;
}
}
if (count > 0)
_logger.LogInformation($"Erased {count} invoice data for store {setting.Key}"); _logger.LogInformation($"Erased {count} invoice data for store {setting.Key}");
setting.Value.LastRunCutoff = cutoffDate; setting.Value.LastRunCutoff = cutoffDate;
await SetCore(setting.Key, setting.Value); await SetCore(setting.Key, setting.Value);
@@ -114,18 +136,30 @@ namespace BTCPayServer.Plugins.DataErasure
_runningLock.Release(); _runningLock.Release();
await Task.Delay(TimeSpan.FromDays(1), cancellationToken); await Task.Delay(TimeSpan.FromHours(1), _cts.Token);
}
try
{
_runningLock.Release();
}
catch (Exception e)
{
} }
} }
private CancellationTokenSource _cts;
public Task StartAsync(CancellationToken cancellationToken) public Task StartAsync(CancellationToken cancellationToken)
{ {
_ = Run(cancellationToken); _cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
_ = Run();
return Task.CompletedTask; return Task.CompletedTask;
} }
public Task StopAsync(CancellationToken cancellationToken) public Task StopAsync(CancellationToken cancellationToken)
{ {
_cts?.Cancel();
return Task.CompletedTask; return Task.CompletedTask;
} }
} }

View File

@@ -7,5 +7,7 @@ namespace BTCPayServer.Plugins.DataErasure
public bool Enabled { get; set; } public bool Enabled { get; set; }
public int DaysToKeep { get; set; } public int DaysToKeep { get; set; }
public DateTimeOffset? LastRunCutoff { get; set; } public DateTimeOffset? LastRunCutoff { get; set; }
public bool EntirelyEraseInvoice { get; set; }
} }
} }

View File

@@ -36,9 +36,24 @@
<input asp-for="DaysToKeep" type="number" class="form-control"/> <input asp-for="DaysToKeep" type="number" class="form-control"/>
</div> </div>
<div class="form-group">
<div class="d-flex align-items-center">
<input asp-for="EntirelyEraseInvoice" type="checkbox" class="btcpay-toggle me-2"/>
<label asp-for="EntirelyEraseInvoice" class="form-label mb-0 me-1">Remove entire invoice from records (instead of just buyer data)</label>
</div>
<div class="alert alert-warning">
<p>
Deleting entire invoices may cause issues with integrations and accounting. Only use this option if you are sure you want to remove the invoice entirely.
</p>
</div>
</div>
@if (Model.LastRunCutoff != null) @if (Model.LastRunCutoff != null)
{ {
<div>Cleared data up to @Model.LastRunCutoff.Value.ToString("g")</div> <div>Cleared data up to @Model.LastRunCutoff.Value.ToString("g")</div>
<div class="form-group">
<button name="command" type="submit" value="cleardate" class="btn btn-danger">Start over</button>
</div>
} }
</div> </div>

View File

@@ -9,7 +9,7 @@
<PropertyGroup> <PropertyGroup>
<Product>Dynamic Rate Limit</Product> <Product>Dynamic Rate Limit</Product>
<Description>Allows you to override the default rate limiting.</Description> <Description>Allows you to override the default rate limiting.</Description>
<Version>1.0.1</Version> <Version>1.0.2</Version>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup> </PropertyGroup>
<!-- Plugin development properties --> <!-- Plugin development properties -->

View File

@@ -10,7 +10,7 @@ public class DynamicRateLimitsPlugin : BaseBTCPayServerPlugin
{ {
public override IBTCPayServerPlugin.PluginDependency[] Dependencies { get; } = public override IBTCPayServerPlugin.PluginDependency[] Dependencies { get; } =
{ {
new() { Identifier = nameof(BTCPayServer), Condition = ">=1.12.0" } new() { Identifier = nameof(BTCPayServer), Condition = ">=2.0.0" }
}; };
public override void Execute(IServiceCollection applicationBuilder) public override void Execute(IServiceCollection applicationBuilder)
{ {

View File

@@ -1,11 +1,6 @@
@using BTCPayServer.Plugins.DynamicRateLimits @using BTCPayServer.Plugins.DynamicRateLimits
@model DynamicRateLimitSettings @model DynamicRateLimitSettings
@{
Layout = "../Shared/_NavLayout.cshtml";
ViewData["NavPartialName"] = "../UIServer/_Nav";
}
<h2 class="mb-4">Rate limit configuration</h2> <h2 class="mb-4">Rate limit configuration</h2>
<form method="post"> <form method="post">

View File

@@ -1,7 +1,13 @@
@using BTCPayServer.Client
@using BTCPayServer.Plugins.DynamicRateLimits @using BTCPayServer.Plugins.DynamicRateLimits
@using Microsoft.AspNetCore.Mvc.TagHelpers
@{ @{
var isActive = ViewContext.RouteData.Values.TryGetValue("Controller", out var controller) && controller is not null && var isActive = ViewContext.RouteData.Values.TryGetValue("Controller", out var controller) && controller is not null &&
nameof(DynamicRatesLimiterController).StartsWith(controller?.ToString(), StringComparison.InvariantCultureIgnoreCase); nameof(DynamicRatesLimiterController).StartsWith(controller?.ToString(), StringComparison.InvariantCultureIgnoreCase);
} }
<a class="nav-link @(isActive ? "active" : string.Empty)" asp-action="Update" asp-controller="DynamicRatesLimiter">Rate Limits</a>
<li class="nav-item nav-item-sub" permission="@Policies.CanModifyServerSettings">
<a class="nav-link @(isActive ? "active" : string.Empty)" asp-action="Update" asp-controller="DynamicRatesLimiter">Rate Limits</a>
</li>

View File

@@ -9,7 +9,7 @@
<PropertyGroup> <PropertyGroup>
<Product>Dynamic Reports</Product> <Product>Dynamic Reports</Product>
<Description>Allows you to create custom reports using SQL.</Description> <Description>Allows you to create custom reports using SQL.</Description>
<Version>1.0.1</Version> <Version>1.0.2</Version>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup> </PropertyGroup>
<!-- Plugin development properties --> <!-- Plugin development properties -->

View File

@@ -10,7 +10,7 @@ public class DynamicReportsPlugin : BaseBTCPayServerPlugin
{ {
public override IBTCPayServerPlugin.PluginDependency[] Dependencies { get; } = public override IBTCPayServerPlugin.PluginDependency[] Dependencies { get; } =
{ {
new() { Identifier = nameof(BTCPayServer), Condition = ">=1.13.0" } new() { Identifier = nameof(BTCPayServer), Condition = ">=2.0.0" }
}; };
public override void Execute(IServiceCollection applicationBuilder) public override void Execute(IServiceCollection applicationBuilder)
{ {

View File

@@ -21,19 +21,15 @@ public class PostgresReportProvider : ReportProvider
public DynamicReportsSettings.DynamicReportSetting Setting { get; set; } public DynamicReportsSettings.DynamicReportSetting Setting { get; set; }
private readonly ApplicationDbContextFactory _dbContextFactory; private readonly ApplicationDbContextFactory _dbContextFactory;
private readonly IOptions<DatabaseOptions> _options;
private readonly IHttpContextAccessor _httpContextAccessor; private readonly IHttpContextAccessor _httpContextAccessor;
public PostgresReportProvider( ApplicationDbContextFactory dbContextFactory, public PostgresReportProvider( ApplicationDbContextFactory dbContextFactory, IHttpContextAccessor httpContextAccessor)
IOptions<DatabaseOptions> options, IHttpContextAccessor httpContextAccessor)
{ {
_dbContextFactory = dbContextFactory; _dbContextFactory = dbContextFactory;
_options = options;
_httpContextAccessor = httpContextAccessor; _httpContextAccessor = httpContextAccessor;
} }
public override bool IsAvailable() public override bool IsAvailable()
{ {
return _options.Value.DatabaseType == DatabaseType.Postgres && return Setting.AllowForNonAdmins || _httpContextAccessor.HttpContext?.User.IsInRole(Roles.ServerAdmin) is true;
Setting.AllowForNonAdmins || _httpContextAccessor.HttpContext?.User.IsInRole(Roles.ServerAdmin) is true;
} }
public override async Task Query(QueryContext queryContext, CancellationToken cancellation) public override async Task Query(QueryContext queryContext, CancellationToken cancellation)
{ {

View File

@@ -10,8 +10,6 @@
@inject DynamicReportService DynamicReportService @inject DynamicReportService DynamicReportService
@{ @{
Layout = "../Shared/_NavLayout.cshtml";
ViewData["NavPartialName"] = "../UIServer/_Nav";
var storeId = ScopeProvider.GetCurrentStoreId(); var storeId = ScopeProvider.GetCurrentStoreId();
var reportName = Context.Request.Query["reportName"].ToString(); var reportName = Context.Request.Query["reportName"].ToString();
reportName = string.IsNullOrEmpty(reportName) ? null : reportName; reportName = string.IsNullOrEmpty(reportName) ? null : reportName;

View File

@@ -1,8 +1,15 @@
@using BTCPayServer.Client
@using BTCPayServer.Plugins.DynamicReports @using BTCPayServer.Plugins.DynamicReports
@using Microsoft.AspNetCore.Mvc.TagHelpers
@{ @{
var isActive = ViewContext.RouteData.Values.TryGetValue("Controller", out var controller) && controller is not null && var isActive = ViewContext.RouteData.Values.TryGetValue("Controller", out var controller) && controller is not null &&
nameof(DynamicReportsController).StartsWith(controller?.ToString(), StringComparison.InvariantCultureIgnoreCase); nameof(DynamicReportsController).StartsWith(controller?.ToString(), StringComparison.InvariantCultureIgnoreCase);
} }
<a class="nav-link @(isActive ? "active" : string.Empty)" asp-action="Update" asp-controller="DynamicReports">Dynamic Reports</a>
<li class="nav-item nav-item-sub" permission="@Policies.CanModifyServerSettings">
<a class="nav-link @(isActive ? "active" : string.Empty)" asp-action="Update" asp-controller="DynamicReports">Dynamic Reports</a>
</li>

View File

@@ -9,7 +9,7 @@
<PropertyGroup> <PropertyGroup>
<Product>File Seller</Product> <Product>File Seller</Product>
<Description>Allows you to sell files through the point of sale/crowdfund apps.</Description> <Description>Allows you to sell files through the point of sale/crowdfund apps.</Description>
<Version>1.0.4</Version> <Version>1.0.5</Version>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup> </PropertyGroup>
<!-- Plugin development properties --> <!-- Plugin development properties -->

View File

@@ -9,8 +9,9 @@ public class FileSellerPlugin : BaseBTCPayServerPlugin
{ {
public override IBTCPayServerPlugin.PluginDependency[] Dependencies { get; } = public override IBTCPayServerPlugin.PluginDependency[] Dependencies { get; } =
{ {
new() { Identifier = nameof(BTCPayServer), Condition = ">=1.12.0" } new() {Identifier = nameof(BTCPayServer), Condition = ">=2.0.0"}
}; };
public override void Execute(IServiceCollection applicationBuilder) public override void Execute(IServiceCollection applicationBuilder)
{ {
applicationBuilder.AddHostedService<FileSellerService>(); applicationBuilder.AddHostedService<FileSellerService>();
@@ -20,7 +21,7 @@ public class FileSellerPlugin : BaseBTCPayServerPlugin
"checkout-end")); "checkout-end"));
applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("FileSeller/FileSellerTemplateEditorItemDetail", applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("FileSeller/FileSellerTemplateEditorItemDetail",
"app-template-editor-item-detail")); "app-template-editor-item-detail"));
base.Execute(applicationBuilder); base.Execute(applicationBuilder);
} }

View File

@@ -6,16 +6,16 @@
@inject UserManager<ApplicationUser> UserManager @inject UserManager<ApplicationUser> UserManager
@inject UserService UserService @inject UserService UserService
@{ @{
var userId = UserManager.GetUserId(User); var user = await UserManager.GetUserAsync(User);
var files = (await StoredFileRepository.GetFiles(new StoredFileRepository.FilesQuery() var files = (await StoredFileRepository.GetFiles(new StoredFileRepository.FilesQuery()
{ {
UserIds = await UserService.IsAdminUser(userId) ? Array.Empty<string>() : new[] {userId}, UserIds = await UserService.IsAdminUser(user) ? Array.Empty<string>() : new[] {user.Id},
})).Select(file => new SelectListItem(file.FileName, file.Id)).Prepend(new SelectListItem("No file", "")); })).Select(file => new SelectListItem(file.FileName, file.Id)).Prepend(new SelectListItem("No file", ""));
} }
<template v-if="editingItem"> <template v-if="editingItem">
<div class="form-group"> <div class="form-group">
<label class="form-label">Downloadable file</label> <label class="form-label">Downloadable file</label>
<select :value="editingItem['file'] || ''" asp-items="files" class="form-select w-auto" v-on:change=" if(event.target.value) editingItem['file']= event.target.value; else delete editingItem['file'];"></select> <select :value="editingItem['file'] || ''" asp-items="files" class="form-select w-auto" v-on:change="if(event.target.value) Vue.set(editingItem, 'file', event.target.value); else Vue.delete(editingItem, 'file');"></select>
<span class="form-text">If a file is selected, when a user buys this item, a download link is generated in the payment receipt once the invoice is settled. <a target="_blank" asp-action="Files" asp-controller="UIServer">Upload files here</a></span> <span class="form-text">If a file is selected, when a user buys this item, a download link is generated in the payment receipt once the invoice is settled. <a target="_blank" asp-action="Files" asp-controller="UIServer">Upload files here</a></span>
</div> </div>
</template> </template>

View File

@@ -9,7 +9,7 @@
<PropertyGroup> <PropertyGroup>
<Product>FixedFloat</Product> <Product>FixedFloat</Product>
<Description>Allows you to embed a FixedFloat conversion screen to allow customers to pay with altcoins.</Description> <Description>Allows you to embed a FixedFloat conversion screen to allow customers to pay with altcoins.</Description>
<Version>1.1.7</Version> <Version>1.1.8</Version>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup> </PropertyGroup>
<!-- Plugin development properties --> <!-- Plugin development properties -->

View File

@@ -9,29 +9,16 @@ namespace BTCPayServer.Plugins.FixedFloat
{ {
public override IBTCPayServerPlugin.PluginDependency[] Dependencies { get; } = public override IBTCPayServerPlugin.PluginDependency[] Dependencies { get; } =
{ {
new() { Identifier = nameof(BTCPayServer), Condition = ">=1.12.0" } new() { Identifier = nameof(BTCPayServer), Condition = ">=2.0.0" }
}; };
public override void Execute(IServiceCollection applicationBuilder) public override void Execute(IServiceCollection applicationBuilder)
{ {
applicationBuilder.AddSingleton<FixedFloatService>(); applicationBuilder.AddSingleton<FixedFloatService>();
applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("FixedFloat/FixedFloatNav", applicationBuilder.AddUIExtension("store-integrations-nav", "FixedFloat/FixedFloatNav");
"store-integrations-nav")); applicationBuilder.AddUIExtension("checkout-payment-method", "FixedFloat/CheckoutPaymentMethodExtension");
// Checkout v2 applicationBuilder.AddUIExtension("checkout-payment", "FixedFloat/CheckoutPaymentExtension");
applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("FixedFloat/CheckoutPaymentMethodExtension",
"checkout-payment-method"));
applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("FixedFloat/CheckoutPaymentExtension",
"checkout-payment"));
// Checkout Classic
applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("FixedFloat/CheckoutContentExtension",
"checkout-bitcoin-post-content"));
applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("FixedFloat/CheckoutContentExtension",
"checkout-lightning-post-content"));
applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("FixedFloat/CheckoutTabExtension",
"checkout-bitcoin-post-tabs"));
applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("FixedFloat/CheckoutTabExtension",
"checkout-lightning-post-tabs"));
applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("FixedFloat/CheckoutEnd",
"checkout-end"));
base.Execute(applicationBuilder); base.Execute(applicationBuilder);
} }
} }

View File

@@ -20,56 +20,70 @@ namespace BTCPayServer.Plugins.FixedFloat
{ {
{"AAVEETH", "Aave (ERC20)"}, {"AAVEETH", "Aave (ERC20)"},
{"ADA", "Cardano"}, {"ADA", "Cardano"},
{"ADABSC", "Cardano (BEP20)"},
{"APT", "Aptos"},
{"ATOM", "Cosmos"}, {"ATOM", "Cosmos"},
{"AVAX", "Avalanche (C-Chain)"}, {"AVAX", "Avalanche (C-Chain)"},
{"BAT", "Basic Attention (ERC20)"}, {"BAT", "Basic Attention (ERC20)"},
{"BCH", "Bitcoin Cash"},
{"BNB", "BNB Beacon Chain (BEP2)"},
{"BSC", "BNB Smart Chain (BEP20)"}, {"BSC", "BNB Smart Chain (BEP20)"},
{"WBNBBSC", "Wrapped BNB (BEP20)"},
{"BTC", "Bitcoin"}, {"BTC", "Bitcoin"},
{"BTCBSC", "Bitcoin (BEP20)"},
{"BTCLN", "Bitcoin (Lightning)"}, {"BTCLN", "Bitcoin (Lightning)"},
{"BTT", "BitTorrent"}, {"BTT", "BitTorrent"},
{"BUSD", "Binance USD (BEP2)"},
{"BUSDBSC", "Binance USD (BEP20)"},
{"BUSDETH", "Binance USD (ERC20)"},
{"CAKE", "PancakeSwap (BEP20)"}, {"CAKE", "PancakeSwap (BEP20)"},
{"DAIBSC", "DAI (BEP20)"},
{"DAIETH", "DAI (ERC20)"}, {"DAIETH", "DAI (ERC20)"},
{"DAIMATIC", "DAI (Polygon)"},
{"DASH", "Dash"},
{"DOGE", "Dogecoin"}, {"DOGE", "Dogecoin"},
{"DOT", "Polkadot"}, {"DOT", "Polkadot"},
{"EOS", "EOS"}, {"EOS", "EOS"},
{"ETC", "Ethereum Classic"},
{"ETH", "Ethereum"}, {"ETH", "Ethereum"},
{"ETHARBITRUM", "Ethereum (Arbitrum)"},
{"ETHBASE", "Ethereum (Base)"},
{"ETHZKSYNC", "Ethereum (ZkSync)"},
{"WETHARBITRUM", "Wrapped ETH (Arbitrum)"},
{"WETHETH", "Wrapped ETH (ERC20)"},
{"FTM", "Fantom"}, {"FTM", "Fantom"},
{"KCS", "KuCoin Token"},
{"LINK", "Chainlink (ERC20)"}, {"LINK", "Chainlink (ERC20)"},
{"LTC", "Litecoin"}, {"LTC", "Litecoin"},
{"MANAETH", "Decentraland (ERC20)"}, {"MANAETH", "Decentraland (ERC20)"},
{"MATIC", "Polygon"},
{"MATICETH", "Polygon (ERC20)"},
{"MKR", "Maker (ERC20)"}, {"MKR", "Maker (ERC20)"},
{"PAXGETH", "PAX Gold (ERC20)"},
{"POL", "Polygon"},
{"POLETH", "Polygon (ERC20)"},
{"SHIB", "SHIBA INU (ERC20)"}, {"SHIB", "SHIBA INU (ERC20)"},
{"SHIBBSC", "SHIBA INU (BEP20)"},
{"SOL", "Solana"}, {"SOL", "Solana"},
{"WSOL", "Wrapped SOL (Solana)"},
{"TON", "Toncoin"}, {"TON", "Toncoin"},
{"TRX", "Tron"}, {"TRX", "Tron"},
{"TUSD", "TrueUSD (ERC20)"}, {"TUSD", "TrueUSD (ERC20)"},
{"TWT", "Trust Wallet Token (BEP2)"},
{"TWTBSC", "Trust Wallet Token (BEP20)"}, {"TWTBSC", "Trust Wallet Token (BEP20)"},
{"USDCARBITRUM", "USD Coin (Arbitrum)"},
{"USDCBSC", "USD Coin (BEP20)"},
{"USDCETH", "USD Coin (ERC20)"}, {"USDCETH", "USD Coin (ERC20)"},
{"USDCMATIC", "USD Coin (Polygon)"},
{"USDCSOL", "USD Coin (Solana)"}, {"USDCSOL", "USD Coin (Solana)"},
{"USDCTRC", "USD Coin (TRC20)"},
{"USDP", "Pax Dollar (ERC20)"}, {"USDP", "Pax Dollar (ERC20)"},
{"USDT", "Tether (ERC20)"}, {"USDT", "Tether (ERC20)"},
{"USDTBSC", "Tether (BEP20)"},
{"USDTMATIC", "Tether (Polygon)"},
{"USDTSOL", "Tether (Solana)"}, {"USDTSOL", "Tether (Solana)"},
{"USDTTRC", "Tether (TRC20)"}, {"USDTTRC", "Tether (TRC20)"},
{"VET", "VeChain"}, {"VET", "VeChain"},
{"XLM", "Stellar Lumens"},
{"XMR", "Monero"}, {"XMR", "Monero"},
{"XRP", "Ripple"}, {"XRP", "Ripple"},
{"XTZ", "Tezos"}, {"XTZ", "Tezos"},
{"ZEC", "Zcash"}, {"ZEC", "Zcash"},
{"ZRX", "0x (ERC20)"} {"ZRX", "0x (ERC20)"}
}; };
public static List<SelectListItem> AllowedSendingOptionsList => AllowedSendingOptions.Select(o => new SelectListItem(o.Value, o.Key)).ToList();
public static List<SelectListItem> AllowedSendingOptionsList =>
AllowedSendingOptions.Select(o => new SelectListItem(o.Value, o.Key)).ToList();
} }
} }

View File

@@ -2,15 +2,17 @@
@using BTCPayServer.Abstractions.Extensions @using BTCPayServer.Abstractions.Extensions
@using BTCPayServer.Data @using BTCPayServer.Data
@using BTCPayServer.Plugins.FixedFloat @using BTCPayServer.Plugins.FixedFloat
@using BTCPayServer.Services.Invoices
@using BTCPayServer.Services.Stores @using BTCPayServer.Services.Stores
@using Microsoft.AspNetCore.Mvc.TagHelpers @using Microsoft.AspNetCore.Mvc.TagHelpers
@model BTCPayServer.Plugins.FixedFloat.FixedFloatSettings @model BTCPayServer.Plugins.FixedFloat.FixedFloatSettings
@inject BTCPayNetworkProvider BTCPayNetworkProvider @inject BTCPayNetworkProvider BTCPayNetworkProvider
@inject PaymentMethodHandlerDictionary PaymentMethodHandlerDictionary
@{ @{
ViewData.SetActivePage("FixedFloat", "FixedFloat", "FixedFloat"); ViewData.SetActivePage("FixedFloat", "FixedFloat", "FixedFloat");
var store = Context.GetStoreData(); var store = Context.GetStoreData();
var allowedPaymentMethods = store.GetEnabledPaymentIds(BTCPayNetworkProvider) var allowedPaymentMethods = store.GetEnabledPaymentIds()
.Select(pmi => new SelectListItem(pmi.ToPrettyString(), pmi.ToString())) .Select(pmi => new SelectListItem(pmi.ToString(), pmi.ToString()))
.Prepend(new SelectListItem("Any", "")); .Prepend(new SelectListItem("Any", ""));
} }

View File

@@ -1,19 +0,0 @@
@using BTCPayServer.Plugins.FixedFloat
@model BTCPayServer.Models.InvoicingModels.PaymentModel
@inject FixedFloatService FixedFloatService
@{
var storeId = Model.StoreId;
var settings = await FixedFloatService.GetFixedFloatForStore(storeId);
if (settings?.Enabled is true)
{
<div id="FixedFloat" class="bp-view payment manual-flow" style="padding:0" :class="{ active: currentTab == 'undefined' || currentTab == 'FixedFloat' }">
<fixed-float inline-template
:to-currency="srvModel.paymentMethodId"
:to-currency-due="srvModel.btcDue * (1 + (@settings.AmountMarkupPercentage / 100)) "
:to-currency-address="srvModel.btcAddress">
<iframe :src="url" style="min-height:600px;width:100%;border:none" allowtransparency="true"></iframe>
</fixed-float>
</div>
}
}

View File

@@ -1,12 +0,0 @@
@using BTCPayServer.Plugins.FixedFloat
@using Newtonsoft.Json
@using Newtonsoft.Json.Linq
@inject FixedFloatService FixedFloatService
@{
var storeId = ((JObject)JObject.Parse(JsonConvert.SerializeObject(Model)))["StoreId"].Value<string>();
var settings = await FixedFloatService.GetFixedFloatForStore(storeId);
if (settings?.Enabled is true)
{
<script src="~/Resources/js/fixedFloatComponent.js"></script>
}
}

View File

@@ -1,10 +1,15 @@
@using BTCPayServer.Abstractions.TagHelpers
@using BTCPayServer.Payments
@using BTCPayServer.Plugins.FixedFloat @using BTCPayServer.Plugins.FixedFloat
@inject FixedFloatService FixedFloatService @inject FixedFloatService FixedFloatService
@model BTCPayServer.Models.InvoicingModels.PaymentModel @model BTCPayServer.Models.InvoicingModels.CheckoutModel
@{ @{
var storeId = Model.StoreId; var storeId = Model.StoreId;
var settings = await FixedFloatService.GetFixedFloatForStore(storeId); var settings = await FixedFloatService.GetFixedFloatForStore(storeId);
var preferredTargetPaymentMethodId = string.IsNullOrEmpty(settings?.PreferredTargetPaymentMethodId) ? null : Model.AvailableCryptos.Any(crypto => crypto.PaymentMethodId == settings.PreferredTargetPaymentMethodId) ? settings.PreferredTargetPaymentMethodId : null; var preferredTargetPaymentMethodId =
string.IsNullOrEmpty(settings?.PreferredTargetPaymentMethodId) ? null :
Model.AvailablePaymentMethods.Any(crypto => crypto.PaymentMethodId.ToString() == PaymentMethodId.TryParse(settings.PreferredTargetPaymentMethodId)?.ToString()) ?
settings.PreferredTargetPaymentMethodId : null;
} }
@if (settings?.Enabled is true) @if (settings?.Enabled is true)
{ {
@@ -41,7 +46,7 @@
const result = this.$parent.paymentMethodId === "FixedFloat"; const result = this.$parent.paymentMethodId === "FixedFloat";
if(this.preferredToCurrency && this.model.paymentMethodId !== this.preferredToCurrency){ if(this.preferredToCurrency && this.model.paymentMethodId !== this.preferredToCurrency){
if (this.model.onChainWithLnInvoiceFallback && this.model.paymentMethodId === "BTC"){ if (this.model.onChainWithLnInvoiceFallback && this.model.paymentMethodId === "BTC-CHAIN"){
return result; return result;
} }
this.$parent.paymentMethodId = this.preferredToCurrency; this.$parent.paymentMethodId = this.preferredToCurrency;
@@ -53,7 +58,7 @@
return result; return result;
}, },
lightning () { lightning () {
if (!this.model.onChainWithLnInvoiceFallback || this.model.paymentMethodId !== "BTC"){ if (!this.model.onChainWithLnInvoiceFallback || this.model.paymentMethodId !== "BTC-CHAIN"){
return null; return null;
} }
const index = this.model.invoiceBitcoinUrl.indexOf("lightning="); const index = this.model.invoiceBitcoinUrl.indexOf("lightning=");
@@ -63,9 +68,9 @@
return this.model.invoiceBitcoinUrl.slice(index + "lightning=".length); return this.model.invoiceBitcoinUrl.slice(index + "lightning=".length);
}, },
url () { url () {
debugger;
const address= this.lightning || this.model.btcAddress; const address= this.lightning || this.model.address;
return "https://widget.fixedfloat.com/?" + return "https://widget.ff.io/?" +
`to=${this.settleMethodId}` + `to=${this.settleMethodId}` +
"&lockReceive=true&ref=fkbyt39c" + "&lockReceive=true&ref=fkbyt39c" +
`&address=${address}` + `&address=${address}` +
@@ -76,9 +81,9 @@
return this.model.paymentMethodId; return this.model.paymentMethodId;
}, },
settleMethodId () { settleMethodId () {
return this.currency.endsWith('LightningLike') || this.currency.endsWith('LNURLPay') || this.lightning return this.currency.endsWith('LN') || this.currency.endsWith('LNURL') || this.lightning
? 'BTCLN' ? 'BTCLN'
: this.currency.replace('_BTCLike', '').replace('_MoneroLike', '').replace('_ZcashLike', '').toUpperCase(); : this.currency.replace('-CHAIN', '').replace('_CHAIN', '').toUpperCase();
}, },
explicitQuery (){ explicitQuery (){
const isExplicit = !!this.explicitId; const isExplicit = !!this.explicitId;
@@ -91,7 +96,7 @@
: `&lockType=true&hideType=true&lockAmount=true&toAmount=${this.amountDue}`; : `&lockType=true&hideType=true&lockAmount=true&toAmount=${this.amountDue}`;
}, },
amountDue () { amountDue () {
return this.model.btcDue * (1 + (markupPercentage / 100)); return this.model.due * (1 + (markupPercentage / 100));
} }
} }
}); });

View File

@@ -1,5 +1,5 @@
@using BTCPayServer.Plugins.FixedFloat @using BTCPayServer.Plugins.FixedFloat
@model BTCPayServer.Models.InvoicingModels.PaymentModel @model BTCPayServer.Models.InvoicingModels.CheckoutModel
@inject FixedFloatService FixedFloatService @inject FixedFloatService FixedFloatService
@{ @{
const string id = "FixedFloat"; const string id = "FixedFloat";

View File

@@ -1,13 +0,0 @@
@using BTCPayServer.Plugins.FixedFloat
@model BTCPayServer.Models.InvoicingModels.PaymentModel
@inject FixedFloatService FixedFloatService
@{
var storeId = Model.StoreId;
var settings = await FixedFloatService.GetFixedFloatForStore(storeId);
if (settings?.Enabled is true)
{
<div class="payment-tabs__tab py-0" id="FixedFloat-tab" v-on:click="switchTab('FixedFloat')" v-bind:class="{ 'active': currentTab == 'FixedFloat'}">
<span>{{$t("Altcoins (FixedFloat)")}}</span>
</div>
}
}

View File

@@ -1,43 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk.Razor">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<LangVersion>10</LangVersion>
</PropertyGroup>
<!-- Plugin specific properties -->
<PropertyGroup>
<Product>LDK</Product>
<Description>The way lightning's meant to be</Description>
<Version>1.0.0</Version>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup>
<!-- Plugin development properties -->
<PropertyGroup>
<AddRazorSupportForMvc>true</AddRazorSupportForMvc>
<PreserveCompilationContext>false</PreserveCompilationContext>
<GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>
</PropertyGroup>
<!-- This will make sure that referencing BTCPayServer doesn't put any artifact in the published directory -->
<ItemDefinitionGroup>
<ProjectReference>
<Properties>StaticWebAssetsEnabled=false</Properties>
<Private>false</Private>
<ExcludeAssets>runtime;native;build;buildTransitive;contentFiles</ExcludeAssets>
</ProjectReference>
</ItemDefinitionGroup>
<ItemGroup>
<EmbeddedResource Include="Resources\**" />
<ProjectReference Include="..\..\submodules\btcpayserver\BTCPayServer\BTCPayServer.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="Resources" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="org.ldk" Version="0.0.118-alpha1" />
</ItemGroup>
</Project>

View File

@@ -1,232 +0,0 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Amazon.Runtime.Internal.Util;
using BTCPayServer;
using BTCPayServer.Abstractions.Models;
using BTCPayServer.Configuration;
using BTCPayServer.Services;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using NBitcoin;
using NBXplorer;
using org.ldk.enums;
using org.ldk.structs;
using enums_Network = org.ldk.enums.Network;
using ILogger = Microsoft.Extensions.Logging.ILogger;
using Logger = org.ldk.structs.Logger;
using Network = NBitcoin.Network;
using OutPoint = org.ldk.structs.OutPoint;
using Path = System.IO.Path;
public class LDKService : IHostedService, PersistInterface, BroadcasterInterfaceInterface, FeeEstimatorInterface, EventHandlerInterface, LoggerInterface, FilterInterface
{
private readonly ILogger<LDKService> _logger;
private readonly IFeeProviderFactory _feeProviderFactory;
private readonly IOptions<DataDirectories> _dataDirectories;
private readonly BTCPayNetwork _network;
private readonly ExplorerClient _explorerClient;
private readonly string _workDir;
private readonly enums_Network _ldkNetwork;
private readonly Logger _ldklogger;
private readonly FeeEstimator _ldkfeeEstimator;
private readonly BroadcasterInterface _ldkbroadcaster;
private readonly Persist _ldkpersist;
private readonly Filter _ldkfilter;
private readonly NetworkGraph _ldkNetworkGraph;
private readonly ChainMonitor _ldkChainMonitor;
public LDKService(BTCPayNetworkProvider btcPayNetworkProvider,
ExplorerClientProvider explorerClientProvider,
ILogger<LDKService> logger,
IFeeProviderFactory feeProviderFactory,
IOptions<DataDirectories> dataDirectories)
{
_logger = logger;
_feeProviderFactory = feeProviderFactory;
_dataDirectories = dataDirectories;
_network = btcPayNetworkProvider.GetNetwork<BTCPayNetwork>("BTC");
_explorerClient = explorerClientProvider.GetExplorerClient(_network);
_workDir = GetWorkDir();
Directory.CreateDirectory(_workDir);
_ldkNetwork = GetLdkNetwork(_network.NBitcoinNetwork);
_ldklogger = Logger.new_impl(this);
_ldkfeeEstimator = FeeEstimator.new_impl(this);
_ldkbroadcaster = BroadcasterInterface.new_impl(this);
_ldkpersist = Persist.new_impl(this);
_ldkfilter = Filter.new_impl(this);
_ldkNetworkGraph = NetworkGraph.of(_ldkNetwork, _ldklogger);
_ldkChainMonitor = ChainMonitor.of( Option_FilterZ.Option_FilterZ_Some.some(_ldkfilter), _ldkbroadcaster, _ldklogger, _ldkfeeEstimator, _ldkpersist);
}
private static enums_Network GetLdkNetwork(Network network)
{
enums_Network? ldkNetwork = null;
if (network.ChainName == ChainName.Mainnet)
ldkNetwork = org.ldk.enums.Network.LDKNetwork_Bitcoin;
else if (network.ChainName == ChainName.Testnet)
ldkNetwork = org.ldk.enums.Network.LDKNetwork_Testnet;
else if (network.ChainName == ChainName.Regtest)
ldkNetwork = org.ldk.enums.Network.LDKNetwork_Regtest;
return ldkNetwork ?? throw new NotSupportedException();
}
private string GetWorkDir()
{
var dir = _dataDirectories.Value.DataDir;
return Path.Combine(dir, "Plugins", "LDK");
}
public async Task StartAsync(CancellationToken cancellationToken)
{
}
public async Task StopAsync(CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public int get_est_sat_per_1000_weight(ConfirmationTarget confirmation_target)
{
var feeProvider = _feeProviderFactory.CreateFeeProvider(_network);
var targetBlocks = confirmation_target switch
{
ConfirmationTarget.LDKConfirmationTarget_OnChainSweep => 30, // High priority (10-50 blocks)
ConfirmationTarget
.LDKConfirmationTarget_MaxAllowedNonAnchorChannelRemoteFee =>
20, // Moderate to high priority (small multiple of high-priority estimate)
ConfirmationTarget
.LDKConfirmationTarget_MinAllowedAnchorChannelRemoteFee =>
12, // Moderate priority (long-term mempool minimum or medium-priority)
ConfirmationTarget
.LDKConfirmationTarget_MinAllowedNonAnchorChannelRemoteFee =>
12, // Moderate priority (medium-priority feerate)
ConfirmationTarget.LDKConfirmationTarget_AnchorChannelFee => 6, // Lower priority (can be bumped later)
ConfirmationTarget
.LDKConfirmationTarget_NonAnchorChannelFee => 20, // Moderate to high priority (high-priority feerate)
ConfirmationTarget.LDKConfirmationTarget_ChannelCloseMinimum => 144, // Within a day or so (144-250 blocks)
_ => throw new ArgumentOutOfRangeException(nameof(confirmation_target), confirmation_target, null)
};
return (int) Math.Max(253, feeProvider.GetFeeRateAsync(targetBlocks).GetAwaiter().GetResult().FeePerK.Satoshi);
}
public void log(Record record)
{
var level = record.get_level() switch
{
Level.LDKLevel_Trace => LogLevel.Trace,
Level.LDKLevel_Debug => LogLevel.Debug,
Level.LDKLevel_Info => LogLevel.Information,
Level.LDKLevel_Warn => LogLevel.Warning,
Level.LDKLevel_Error => LogLevel.Error,
Level.LDKLevel_Gossip => LogLevel.Trace,
};
_logger.Log(level, $"[{record.get_module_path()}] {record.get_args()}");
}
public void broadcast_transactions(byte[][] txs)
{
foreach (var tx in txs)
{
var loadedTx = Transaction.Load(tx, _explorerClient.Network.NBitcoinNetwork);
_explorerClient.Broadcast(loadedTx);
}
}
public ChannelMonitorUpdateStatus persist_new_channel(OutPoint channel_id, ChannelMonitor data,
MonitorUpdateId update_id)
{
var name = Convert.ToHexString(channel_id.write());
File.WriteAllBytes(Path.Combine(_workDir, name), data.write());
return ChannelMonitorUpdateStatus.LDKChannelMonitorUpdateStatus_Completed;
}
public ChannelMonitorUpdateStatus update_persisted_channel(OutPoint channel_id, ChannelMonitorUpdate update,
ChannelMonitor data, MonitorUpdateId update_id)
{
var name = Convert.ToHexString(channel_id.write());
File.WriteAllBytes(Path.Combine(_workDir, name), data.write());
return ChannelMonitorUpdateStatus.LDKChannelMonitorUpdateStatus_Completed;
}
public void handle_event(Event _event)
{
switch (_event)
{
case Event.Event_BumpTransaction eventBumpTransaction:
switch (eventBumpTransaction.bump_transaction)
{
case BumpTransactionEvent.BumpTransactionEvent_ChannelClose bumpTransactionEventChannelClose:
break;
case BumpTransactionEvent.BumpTransactionEvent_HTLCResolution bumpTransactionEventHtlcResolution:
break;
default:
throw new ArgumentOutOfRangeException();
}
break;
case Event.Event_ChannelClosed eventChannelClosed:
break;
case Event.Event_ChannelPending eventChannelPending:
break;
case Event.Event_ChannelReady eventChannelReady:
break;
case Event.Event_DiscardFunding eventDiscardFunding:
break;
case Event.Event_FundingGenerationReady eventFundingGenerationReady:
break;
case Event.Event_HTLCHandlingFailed eventHtlcHandlingFailed:
break;
case Event.Event_HTLCIntercepted eventHtlcIntercepted:
break;
case Event.Event_InvoiceRequestFailed eventInvoiceRequestFailed:
break;
case Event.Event_OpenChannelRequest eventOpenChannelRequest:
break;
case Event.Event_PaymentClaimable eventPaymentClaimable:
break;
case Event.Event_PaymentClaimed eventPaymentClaimed:
break;
case Event.Event_PaymentFailed eventPaymentFailed:
break;
case Event.Event_PaymentForwarded eventPaymentForwarded:
break;
case Event.Event_PaymentPathFailed eventPaymentPathFailed:
break;
case Event.Event_PaymentPathSuccessful eventPaymentPathSuccessful:
break;
case Event.Event_PaymentSent eventPaymentSent:
break;
case Event.Event_PendingHTLCsForwardable eventPendingHtlCsForwardable:
break;
case Event.Event_ProbeFailed eventProbeFailed:
break;
case Event.Event_ProbeSuccessful eventProbeSuccessful:
break;
case Event.Event_SpendableOutputs eventSpendableOutputs:
break;
default:
throw new ArgumentOutOfRangeException(nameof(_event));
}
}
public void register_tx(byte[] txid, byte[] script_pubkey)
{
throw new NotImplementedException();
}
public void register_output(WatchedOutput output)
{
throw new NotImplementedException();
}
}

View File

@@ -12,7 +12,7 @@
<PropertyGroup> <PropertyGroup>
<Product>Liquid+</Product> <Product>Liquid+</Product>
<Description>Enhanced support for the liquid network.</Description> <Description>Enhanced support for the liquid network.</Description>
<Version>1.1.4</Version> <Version>1.1.5</Version>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup> </PropertyGroup>

View File

@@ -10,7 +10,10 @@ using BTCPayServer.Abstractions.Extensions;
using BTCPayServer.Abstractions.Models; using BTCPayServer.Abstractions.Models;
using BTCPayServer.Client; using BTCPayServer.Client;
using BTCPayServer.Common; using BTCPayServer.Common;
using BTCPayServer.Data;
using BTCPayServer.Plugins.Altcoins; using BTCPayServer.Plugins.Altcoins;
using BTCPayServer.Services.Invoices;
using BTCPayServer.Services.Stores;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ViewFeatures; using Microsoft.AspNetCore.Mvc.ViewFeatures;
@@ -25,20 +28,24 @@ namespace BTCPayServer.Plugins.LiquidPlus.Controllers
[AutoValidateAntiforgeryToken] [AutoValidateAntiforgeryToken]
public class StoreLiquidController : Controller public class StoreLiquidController : Controller
{ {
private readonly PaymentMethodHandlerDictionary _paymentMethodHandlerDictionary;
private readonly StoreRepository _storeRepository;
private readonly BTCPayNetworkProvider _btcPayNetworkProvider; private readonly BTCPayNetworkProvider _btcPayNetworkProvider;
private readonly BTCPayServerClient _client;
private readonly IExplorerClientProvider _explorerClientProvider; private readonly IExplorerClientProvider _explorerClientProvider;
public StoreLiquidController(BTCPayNetworkProvider btcPayNetworkProvider, public StoreLiquidController(PaymentMethodHandlerDictionary paymentMethodHandlerDictionary,
BTCPayServerClient client, IExplorerClientProvider explorerClientProvider) StoreRepository storeRepository, BTCPayNetworkProvider btcPayNetworkProvider,
IExplorerClientProvider explorerClientProvider)
{ {
_paymentMethodHandlerDictionary = paymentMethodHandlerDictionary;
_storeRepository = storeRepository;
_btcPayNetworkProvider = btcPayNetworkProvider; _btcPayNetworkProvider = btcPayNetworkProvider;
_client = client;
_explorerClientProvider = explorerClientProvider; _explorerClientProvider = explorerClientProvider;
} }
[HttpGet("stores/{storeId}/liquid")] [HttpGet("stores/{storeId}/liquid")]
public async Task<IActionResult> GenerateLiquidScript(string storeId, Dictionary<string, BitcoinExtKey> bitcoinExtKeys = null) public async Task<IActionResult> GenerateLiquidScript(string storeId,
Dictionary<string, BitcoinExtKey> bitcoinExtKeys = null)
{ {
Dictionary<string, string> generated = new Dictionary<string, string>(); Dictionary<string, string> generated = new Dictionary<string, string>();
var allNetworks = _btcPayNetworkProvider.GetAll().OfType<ElementsBTCPayNetwork>() var allNetworks = _btcPayNetworkProvider.GetAll().OfType<ElementsBTCPayNetwork>()
@@ -48,13 +55,20 @@ namespace BTCPayServer.Plugins.LiquidPlus.Controllers
.ToArray() .ToArray()
.Distinct(); .Distinct();
Dictionary<string, BitcoinExtKey> privKeys = bitcoinExtKeys ?? new Dictionary<string, BitcoinExtKey>(); Dictionary<string, BitcoinExtKey> privKeys = bitcoinExtKeys ?? new Dictionary<string, BitcoinExtKey>();
var paymentMethods = (await _client.GetStoreOnChainPaymentMethods(storeId))
.Where(settings => allNetworkCodes.Contains(settings.CryptoCode))
.GroupBy(data => _btcPayNetworkProvider.GetNetwork<ElementsBTCPayNetwork>(data.CryptoCode).NetworkCryptoCode);
if (paymentMethods.Any() is false) var store = await _storeRepository.FindStore(storeId);
var pms = store
.GetPaymentMethodConfigs<DerivationSchemeSettings>(_paymentMethodHandlerDictionary)
.Select(pair => (PaymentMethodId: pair.Key, DerivationSchemeSettings: pair.Value,
CryptoCode: pair.Key.ToString().Split("-")[0])).ToArray();
var paymentMethodsGroupedByNetworkCode =
pms
.Where(settings => allNetworkCodes.Contains(settings.CryptoCode))
.GroupBy(data =>
_btcPayNetworkProvider.GetNetwork<ElementsBTCPayNetwork>(data.CryptoCode).NetworkCryptoCode);
if (paymentMethodsGroupedByNetworkCode.Any() is false)
{ {
TempData.SetStatusMessageModel(new StatusMessageModel() TempData.SetStatusMessageModel(new StatusMessageModel()
{ {
@@ -63,12 +77,12 @@ namespace BTCPayServer.Plugins.LiquidPlus.Controllers
}); });
return View(new GenerateLiquidImportScripts()); return View(new GenerateLiquidImportScripts());
} }
foreach (var der in paymentMethods) foreach (var der in paymentMethodsGroupedByNetworkCode)
{ {
var network = _btcPayNetworkProvider.GetNetwork<ElementsBTCPayNetwork>(der.Key); var network = _btcPayNetworkProvider.GetNetwork<ElementsBTCPayNetwork>(der.Key);
var nbxnet = network.NBXplorerNetwork; var nbxnet = network.NBXplorerNetwork;
var sb = new StringBuilder(); var sb = new StringBuilder();
var explorerClient = _explorerClientProvider.GetExplorerClient(der.Key); var explorerClient = _explorerClientProvider.GetExplorerClient(der.Key);
@@ -79,12 +93,12 @@ namespace BTCPayServer.Plugins.LiquidPlus.Controllers
generated.Add(der.Key, sb.ToString()); generated.Add(der.Key, sb.ToString());
continue; continue;
} }
var derivationSchemesForNetwork = der.GroupBy(data => data.DerivationScheme);
var derivationSchemesForNetwork = der.GroupBy(data => data.DerivationSchemeSettings);
foreach (var paymentMethodDerivationScheme in derivationSchemesForNetwork) foreach (var paymentMethodDerivationScheme in derivationSchemesForNetwork)
{ {
var derivatonScheme = var derivatonScheme = paymentMethodDerivationScheme.Key.AccountDerivation;
nbxnet.DerivationStrategyFactory.Parse(paymentMethodDerivationScheme.Key);
var sameWalletCryptoCodes = paymentMethodDerivationScheme.Select(data => data.CryptoCode).ToArray(); var sameWalletCryptoCodes = paymentMethodDerivationScheme.Select(data => data.CryptoCode).ToArray();
var matchedExistingKey = privKeys.Where(pair => sameWalletCryptoCodes.Contains(pair.Key)); var matchedExistingKey = privKeys.Where(pair => sameWalletCryptoCodes.Contains(pair.Key));
BitcoinExtKey key = null; BitcoinExtKey key = null;
@@ -94,22 +108,20 @@ namespace BTCPayServer.Plugins.LiquidPlus.Controllers
} }
else else
{ {
key = await explorerClient.GetMetadataAsync<BitcoinExtKey>(derivatonScheme, key = await explorerClient.GetMetadataAsync<BitcoinExtKey>(derivatonScheme,
WellknownMetadataKeys.AccountHDKey); WellknownMetadataKeys.AccountHDKey);
} }
if (key != null) if (key != null)
{ {
foreach (var paymentMethodData in paymentMethodDerivationScheme) foreach (var paymentMethodData in paymentMethodDerivationScheme)
{ {
privKeys.TryAdd(paymentMethodData.CryptoCode, key); privKeys.TryAdd(paymentMethodData.CryptoCode, key);
} }
} }
var utxos = await explorerClient.GetUTXOsAsync(derivatonScheme, CancellationToken.None); var utxos = await explorerClient.GetUTXOsAsync(derivatonScheme, CancellationToken.None);
foreach (var utxo in utxos.GetUnspentUTXOs()) foreach (var utxo in utxos.GetUnspentUTXOs())
{ {
var addr = nbxnet.CreateAddress(derivatonScheme, utxo.KeyPath, utxo.ScriptPubKey); var addr = nbxnet.CreateAddress(derivatonScheme, utxo.KeyPath, utxo.ScriptPubKey);
@@ -139,21 +151,21 @@ namespace BTCPayServer.Plugins.LiquidPlus.Controllers
{ {
sb.AppendLine("elements-cli stop"); sb.AppendLine("elements-cli stop");
sb.AppendLine("elementsd -rescan"); sb.AppendLine("elementsd -rescan");
} }
generated.Add(der.Key, sb.ToString()); generated.Add(der.Key, sb.ToString());
} }
return View(new GenerateLiquidImportScripts() return View(new GenerateLiquidImportScripts()
{ {
Wallets = paymentMethods.SelectMany(settings => Wallets = paymentMethodsGroupedByNetworkCode.SelectMany(settings =>
settings.Select(data => settings.Select(data =>
new GenerateLiquidImportScripts.GenerateLiquidImportScriptWalletKeyVm() new GenerateLiquidImportScripts.GenerateLiquidImportScriptWalletKeyVm()
{ {
CryptoCode = data.CryptoCode, CryptoCode = data.CryptoCode,
KeyPresent = privKeys.ContainsKey(data.CryptoCode), KeyPresent = privKeys.ContainsKey(data.CryptoCode),
ManualKey = null ManualKey = null
}).ToArray()).ToArray(), }).ToArray()).ToArray(),
Scripts = generated Scripts = generated
}); });
} }
@@ -199,10 +211,9 @@ namespace BTCPayServer.Plugins.LiquidPlus.Controllers
continue; continue;
} }
var der = HttpContext.GetStoreData()
.GetDerivationSchemeSettings(_paymentMethodHandlerDictionary, wallet.CryptoCode).AccountDerivation;
var der = n.NBXplorerNetwork.DerivationStrategyFactory.Parse(
(await _client.GetStoreOnChainPaymentMethod(storeId, wallet.CryptoCode)).DerivationScheme);
if (der.GetExtPubKeys().Count() > 1) if (der.GetExtPubKeys().Count() > 1)
{ {
vm.AddModelError(scripts => scripts.Wallets[index].ManualKey, "cannot handle multsig", this); vm.AddModelError(scripts => scripts.Wallets[index].ManualKey, "cannot handle multsig", this);
@@ -250,7 +261,8 @@ namespace BTCPayServer.Plugins.LiquidPlus.Controllers
return await GenerateLiquidScript(storeId, privKeys); return await GenerateLiquidScript(storeId, privKeys);
} }
public class GenerateLiquidImportScripts
public class GenerateLiquidImportScripts
{ {
public class GenerateLiquidImportScriptWalletKeyVm public class GenerateLiquidImportScriptWalletKeyVm
{ {
@@ -266,6 +278,7 @@ public class GenerateLiquidImportScripts
} }
} }
} }
namespace XX namespace XX
{ {
public static class ModelStateExtensions public static class ModelStateExtensions
@@ -275,9 +288,11 @@ namespace XX
string message, string message,
ControllerBase controller) ControllerBase controller)
{ {
var provider = (ModelExpressionProvider)controller.HttpContext.RequestServices.GetService(typeof(ModelExpressionProvider)); var provider =
(ModelExpressionProvider) controller.HttpContext.RequestServices.GetService(
typeof(ModelExpressionProvider));
var key = provider.GetExpressionText(ex); var key = provider.GetExpressionText(ex);
controller.ModelState.AddModelError(key, message); controller.ModelState.AddModelError(key, message);
} }
} }
} }

View File

@@ -22,7 +22,7 @@ namespace BTCPayServer.Plugins.LiquidPlus
{ {
public override IBTCPayServerPlugin.PluginDependency[] Dependencies { get; } = public override IBTCPayServerPlugin.PluginDependency[] Dependencies { get; } =
{ {
new() {Identifier = nameof(BTCPayServer), Condition = ">=1.12.0"} new() {Identifier = nameof(BTCPayServer), Condition = ">=2.0.0"}
}; };
public override void Execute(IServiceCollection applicationBuilder) public override void Execute(IServiceCollection applicationBuilder)
@@ -31,11 +31,11 @@ namespace BTCPayServer.Plugins.LiquidPlus
if (services.BootstrapServices.GetRequiredService<NBXplorerNetworkProvider>() if (services.BootstrapServices.GetRequiredService<NBXplorerNetworkProvider>()
.GetFromCryptoCode("LBTC") is null || !services.BootstrapServices.GetRequiredService<SelectedChains>().Contains("LBTC")) .GetFromCryptoCode("LBTC") is null || !services.BootstrapServices.GetRequiredService<SelectedChains>().Contains("LBTC"))
return; return;
services.AddSingleton<IUIExtension>(new UIExtension("LiquidNav", "store-integrations-nav")); services.AddUIExtension("store-integrations-nav", "LiquidNav");
services.AddSingleton<IUIExtension>(new UIExtension("OnChainWalletSetupLiquidExtension", services.AddUIExtension("onchain-wallet-setup-post-body", "OnChainWalletSetupLiquidExtension");
"onchain-wallet-setup-post-body")); services.AddUIExtension("server-nav", "CustomLiquidAssetsNavExtension");
services.AddSingleton<IUIExtension>(new UIExtension("CustomLiquidAssetsNavExtension", "server-nav")); services.AddUIExtension("store-nav", "StoreNavLiquidExtension");
services.AddSingleton<IUIExtension>(new UIExtension("StoreNavLiquidExtension", "store-nav"));
services.AddSingleton<CustomLiquidAssetsRepository>(); services.AddSingleton<CustomLiquidAssetsRepository>();
@@ -53,12 +53,11 @@ namespace BTCPayServer.Plugins.LiquidPlus
CryptoCode: "LBTC" CryptoCode: "LBTC"
}) })
.ImplementationInstance; .ImplementationInstance;
var pmi = PaymentTypes.CHAIN.GetPaymentMethodId("LBTC");
var tlProvider = (TransactionLinkProviders.Entry) services.Single(descriptor => var tlProvider = (TransactionLinkProviders.Entry) services.Single(descriptor =>
descriptor.ServiceType == typeof(TransactionLinkProviders.Entry) && descriptor.ServiceType == typeof(TransactionLinkProviders.Entry) &&
descriptor.ImplementationInstance is TransactionLinkProviders.Entry descriptor.ImplementationInstance is TransactionLinkProviders.Entry entry && entry.PaymentMethodId == pmi)
{
PaymentMethodId: {CryptoCode: "LBTC"}
})
.ImplementationInstance; .ImplementationInstance;
settings.Items.ForEach(configuration => settings.Items.ForEach(configuration =>
@@ -80,7 +79,6 @@ namespace BTCPayServer.Plugins.LiquidPlus
NetworkCryptoCode = template.NetworkCryptoCode, NetworkCryptoCode = template.NetworkCryptoCode,
DefaultSettings = template.DefaultSettings, DefaultSettings = template.DefaultSettings,
ElectrumMapping = template.ElectrumMapping, ElectrumMapping = template.ElectrumMapping,
BlockExplorerLink = template.BlockExplorerLink,
ReadonlyWallet = template.ReadonlyWallet, ReadonlyWallet = template.ReadonlyWallet,
SupportLightning = false, SupportLightning = false,
SupportPayJoin = false, SupportPayJoin = false,
@@ -92,7 +90,7 @@ namespace BTCPayServer.Plugins.LiquidPlus
VaultSupported = template.VaultSupported, VaultSupported = template.VaultSupported,
MaxTrackedConfirmation = template.MaxTrackedConfirmation, MaxTrackedConfirmation = template.MaxTrackedConfirmation,
SupportRBF = template.SupportRBF SupportRBF = template.SupportRBF
}).AddTransactionLinkProvider(new PaymentMethodId(code, PaymentTypes.BTCLike), tlProvider.Provider); }).AddTransactionLinkProvider(code, tlProvider.Provider);
}); });
} }
} }

View File

@@ -34,7 +34,7 @@
<ItemGroup> <ItemGroup>
<EmbeddedResource Include="Resources\**" /> <EmbeddedResource Include="Resources\**" />
<ProjectReference Include="..\..\submodules\btcpayserver\BTCPayServer\BTCPayServer.csproj" /> <ProjectReference Include="..\..\submodules\btcpayserver\BTCPayServer\BTCPayServer.csproj" />
<PackageReference Include="AsyncKeyedLock" Version="6.4.2" /> <PackageReference Include="AsyncKeyedLock" Version="7.0.1" />
<PackageReference Include="FlexLabs.EntityFrameworkCore.Upsert" Version="8.0.0" /> <PackageReference Include="FlexLabs.EntityFrameworkCore.Upsert" Version="8.0.0" />
<PackageReference Include="Laraue.EfCoreTriggers.PostgreSql" Version="8.0.3" /> <PackageReference Include="Laraue.EfCoreTriggers.PostgreSql" Version="8.0.3" />
<PackageReference Include="Microsoft.CodeAnalysis.Common" Version="4.10.0" /> <PackageReference Include="Microsoft.CodeAnalysis.Common" Version="4.10.0" />

View File

@@ -1,23 +1,29 @@
using BTCPayServer.Abstractions.Contracts; using System;
using BTCPayServer.Abstractions.Contracts;
using BTCPayServer.Abstractions.Models; using BTCPayServer.Abstractions.Models;
using Laraue.EfCoreTriggers.PostgreSql.Extensions; using BTCPayServer.Data;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure;
namespace BTCPayServer.Plugins.MicroNode; namespace BTCPayServer.Plugins.MicroNode;
public class MicroNodeContextFactory : BaseDbContextFactory<MicroNodeContext> public class MicroNodeContextFactory : BaseDbContextFactory<MicroNodeContext>
{ {
public MicroNodeContextFactory(IOptions<DatabaseOptions> options) : base(options, "BTCPayServer.Plugins.MicroNode") private readonly ILoggerFactory _loggerFactory;
public MicroNodeContextFactory(IOptions<DatabaseOptions> options, ILoggerFactory loggerFactory) : base(options, "BTCPayServer.Plugins.MicroNode")
{ {
_loggerFactory = loggerFactory;
} }
public override MicroNodeContext CreateContext() public override MicroNodeContext CreateContext(Action<NpgsqlDbContextOptionsBuilder> npgsqlOptionsAction = null)
{ {
var builder = new DbContextOptionsBuilder<MicroNodeContext>(); var builder = new DbContextOptionsBuilder<MicroNodeContext>();
ConfigureBuilder(builder); builder.UseLoggerFactory(_loggerFactory);
builder.UsePostgreSqlTriggers(); builder.AddInterceptors(MigrationInterceptor.Instance);
ConfigureBuilder(builder, npgsqlOptionsAction);
return new MicroNodeContext(builder.Options); return new MicroNodeContext(builder.Options);
} }
} }

View File

@@ -8,6 +8,7 @@ using BTCPayServer.Client;
using BTCPayServer.Data; using BTCPayServer.Data;
using BTCPayServer.Payments; using BTCPayServer.Payments;
using BTCPayServer.Payments.Lightning; using BTCPayServer.Payments.Lightning;
using BTCPayServer.Services.Invoices;
using BTCPayServer.Services.Stores; using BTCPayServer.Services.Stores;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
@@ -17,14 +18,17 @@ namespace BTCPayServer.Plugins.MicroNode;
[Route("plugins/micronode")] [Route("plugins/micronode")]
public class MicroNodeController : Controller public class MicroNodeController : Controller
{ {
private readonly PaymentMethodHandlerDictionary _paymentMethodHandlerDictionary;
private readonly MicroNodeService _microNodeService; private readonly MicroNodeService _microNodeService;
private readonly StoreRepository _storeRepository; private readonly StoreRepository _storeRepository;
private readonly BTCPayNetworkProvider _networkProvider; private readonly BTCPayNetworkProvider _networkProvider;
private readonly IAuthorizationService _authorizationService; private readonly IAuthorizationService _authorizationService;
public MicroNodeController(MicroNodeService microNodeService, StoreRepository storeRepository, public MicroNodeController(
PaymentMethodHandlerDictionary paymentMethodHandlerDictionary,MicroNodeService microNodeService, StoreRepository storeRepository,
BTCPayNetworkProvider networkProvider, IAuthorizationService authorizationService) BTCPayNetworkProvider networkProvider, IAuthorizationService authorizationService)
{ {
_paymentMethodHandlerDictionary = paymentMethodHandlerDictionary;
_microNodeService = microNodeService; _microNodeService = microNodeService;
_storeRepository = storeRepository; _storeRepository = storeRepository;
_networkProvider = networkProvider; _networkProvider = networkProvider;
@@ -66,11 +70,8 @@ public class MicroNodeController : Controller
ModelState.AddModelError("masterStoreId", "Master cannot be the same as this store"); ModelState.AddModelError("masterStoreId", "Master cannot be the same as this store");
return View(settings); return View(settings);
} }
var pmi = PaymentTypes.LN.GetPaymentMethodId(network.CryptoCode);
var existing = store.GetSupportedPaymentMethods(_networkProvider).OfType<LightningSupportedPaymentMethod>() var existing = store.GetPaymentMethodConfig<LightningPaymentMethodConfig>(pmi, _paymentMethodHandlerDictionary);
.FirstOrDefault(method =>
method.PaymentId.PaymentType == LightningPaymentType.Instance &&
method.PaymentId.CryptoCode == network.CryptoCode);
var isSet = settings?.Key is not null; var isSet = settings?.Key is not null;
settings ??= new MicroNodeStoreSettings(); settings ??= new MicroNodeStoreSettings();
settings.Key ??= Guid.NewGuid().ToString(); settings.Key ??= Guid.NewGuid().ToString();
@@ -118,12 +119,10 @@ public class MicroNodeController : Controller
} }
existing ??= new LightningSupportedPaymentMethod() existing ??= new();
{
CryptoCode = "BTC"
};
existing.SetLightningUrl(mlc); existing.SetLightningUrl(mlc);
store.SetSupportedPaymentMethod(existing);
store.SetPaymentMethodConfig(_paymentMethodHandlerDictionary[pmi], existing);
await _microNodeService.Set(storeId, settings, masterStoreId); await _microNodeService.Set(storeId, settings, masterStoreId);
@@ -145,7 +144,7 @@ public class MicroNodeController : Controller
if (isStoreSetToThisMicro) if (isStoreSetToThisMicro)
{ {
store.SetSupportedPaymentMethod(existing.PaymentId, null); store.SetPaymentMethodConfig(_paymentMethodHandlerDictionary[pmi], null);
await _storeRepository.UpdateStore(store); await _storeRepository.UpdateStore(store);
} }

View File

@@ -11,7 +11,7 @@ public class MicroNodePlugin:BaseBTCPayServerPlugin
{ {
public override IBTCPayServerPlugin.PluginDependency[] Dependencies { get; } = public override IBTCPayServerPlugin.PluginDependency[] Dependencies { get; } =
{ {
new () { Identifier = nameof(BTCPayServer), Condition = ">=1.12.0" } new () { Identifier = nameof(BTCPayServer), Condition = ">=2.0.0" }
}; };

View File

@@ -15,7 +15,9 @@ using BTCPayServer.HostedServices;
using BTCPayServer.Lightning; using BTCPayServer.Lightning;
using BTCPayServer.Payments; using BTCPayServer.Payments;
using BTCPayServer.Payments.Lightning; using BTCPayServer.Payments.Lightning;
using BTCPayServer.Payouts;
using BTCPayServer.Services; using BTCPayServer.Services;
using BTCPayServer.Services.Invoices;
using BTCPayServer.Services.Stores; using BTCPayServer.Services.Stores;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
@@ -34,6 +36,7 @@ public class MicroNodeService : EventHostedServiceBase
private readonly ILogger<MicroNodeService> _logger; private readonly ILogger<MicroNodeService> _logger;
private readonly PullPaymentHostedService _pullPaymentHostedService; private readonly PullPaymentHostedService _pullPaymentHostedService;
private readonly IOptions<LightningNetworkOptions> _lightningNetworkOptions; private readonly IOptions<LightningNetworkOptions> _lightningNetworkOptions;
private readonly PaymentMethodHandlerDictionary _paymentMethodHandlerDictionary;
private static readonly ConcurrentDictionary<string, long> ExpectedCounter = new(); private static readonly ConcurrentDictionary<string, long> ExpectedCounter = new();
private readonly TaskCompletionSource _init = new(); private readonly TaskCompletionSource _init = new();
private Dictionary<string, MicroNodeSettings> _ownerSettings; private Dictionary<string, MicroNodeSettings> _ownerSettings;
@@ -53,6 +56,7 @@ public class MicroNodeService : EventHostedServiceBase
EventAggregator eventAggregator, EventAggregator eventAggregator,
PullPaymentHostedService pullPaymentHostedService, PullPaymentHostedService pullPaymentHostedService,
IOptions<LightningNetworkOptions> lightningNetworkOptions, IOptions<LightningNetworkOptions> lightningNetworkOptions,
PaymentMethodHandlerDictionary paymentMethodHandlerDictionary,
IServiceProvider serviceProvider) : base(eventAggregator, logger) IServiceProvider serviceProvider) : base(eventAggregator, logger)
{ {
_network = btcPayNetworkProvider.BTC; _network = btcPayNetworkProvider.BTC;
@@ -63,6 +67,7 @@ public class MicroNodeService : EventHostedServiceBase
_logger = logger; _logger = logger;
_pullPaymentHostedService = pullPaymentHostedService; _pullPaymentHostedService = pullPaymentHostedService;
_lightningNetworkOptions = lightningNetworkOptions; _lightningNetworkOptions = lightningNetworkOptions;
_paymentMethodHandlerDictionary = paymentMethodHandlerDictionary;
_serviceProvider = serviceProvider; _serviceProvider = serviceProvider;
} }
@@ -124,17 +129,20 @@ public class MicroNodeService : EventHostedServiceBase
{ {
var b = payout.GetBlob(_btcPayNetworkJsonSerializerSettings); var b = payout.GetBlob(_btcPayNetworkJsonSerializerSettings);
List<MicroTransaction> res = new(); List<MicroTransaction> res =
res.Add(new MicroTransaction() [
{ new()
Id = payout.Id, {
AccountId = key, Id = payout.Id,
Amount = -LightMoney.Coins(b.CryptoAmount.Value).MilliSatoshi, AccountId = key,
Accounted = payout.State != PayoutState.Cancelled, Amount = -LightMoney.Coins(payout.Amount!.Value).MilliSatoshi,
Active = payout.State is PayoutState.AwaitingApproval or PayoutState.AwaitingPayment Accounted = payout.State != PayoutState.Cancelled,
or PayoutState.InProgress, Active = payout.State is PayoutState.AwaitingApproval or PayoutState.AwaitingPayment
Type = "Payout" or PayoutState.InProgress,
}); Type = "Payout"
}
];
if (b.Metadata?.TryGetValue("Fee", out var microNode) is true && microNode.Value<decimal>() is { } payoutFee) if (b.Metadata?.TryGetValue("Fee", out var microNode) is true && microNode.Value<decimal>() is { } payoutFee)
{ {
@@ -230,7 +238,7 @@ public class MicroNodeService : EventHostedServiceBase
await using var ctx = _microNodeContextFactory.CreateContext(); await using var ctx = _microNodeContextFactory.CreateContext();
for (int i = 0; i < 5; i++) for (int i = 0; i < 5; i++)
{ {
var account = await ctx.MicroAccounts.FindAsync(key); var account = await ctx.MicroAccounts.FindAsync(key, cancellation);
if (account is null) if (account is null)
{ {
return null; return null;
@@ -336,9 +344,9 @@ public class MicroNodeService : EventHostedServiceBase
return null; return null;
} }
var lightningConnectionString = store.GetSupportedPaymentMethods(_btcPayNetworkProvider) var pmi = PaymentTypes.LN.GetPaymentMethodId(_network.CryptoCode);
.OfType<LightningSupportedPaymentMethod>() var lightningConnectionString = store.GetPaymentMethodConfig<LightningPaymentMethodConfig>(pmi, _paymentMethodHandlerDictionary)
.FirstOrDefault(method => method.CryptoCode == _network.CryptoCode)?.CreateLightningClient(_network, ?.CreateLightningClient(_network,
_lightningNetworkOptions.Value, _serviceProvider.GetService<LightningClientFactoryService>()); _lightningNetworkOptions.Value, _serviceProvider.GetService<LightningClientFactoryService>());
return lightningConnectionString; return lightningConnectionString;
} }
@@ -616,7 +624,7 @@ public class MicroNodeService : EventHostedServiceBase
StoreId = masterClients.Key, StoreId = masterClients.Key,
Destination = new LNURLPayClaimDestinaton(destination), Destination = new LNURLPayClaimDestinaton(destination),
PreApprove = true, PreApprove = true,
PaymentMethodId = new PaymentMethodId("BTC", LightningPaymentType.Instance), PayoutMethodId = PayoutTypes.LN.GetPayoutMethodId("BTC"),
Metadata = JObject.FromObject(new Metadata = JObject.FromObject(new
{ {
Source = $"MicroNode on store {storeId.Key}" Source = $"MicroNode on store {storeId.Key}"
@@ -667,7 +675,7 @@ public class MicroNodeService : EventHostedServiceBase
await base.StopAsync(cancellationToken); await base.StopAsync(cancellationToken);
} }
private ConcurrentDictionary<string, string> _keyToMasterStoreId = new(); private readonly ConcurrentDictionary<string, string> _keyToMasterStoreId = new();
public async Task Set(string storeId, MicroNodeStoreSettings? settings, string? masterStoreId = null) public async Task Set(string storeId, MicroNodeStoreSettings? settings, string? masterStoreId = null)
{ {

View File

@@ -11,7 +11,7 @@
<PropertyGroup> <PropertyGroup>
<Product>Nostr</Product> <Product>Nostr</Product>
<Description>NIP5 addresses, Zap support, Nostr Wallet Connect Lightning support</Description> <Description>NIP5 addresses, Zap support, Nostr Wallet Connect Lightning support</Description>
<Version>1.1.13</Version> <Version>1.1.14</Version>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup> </PropertyGroup>
<!-- Plugin development properties --> <!-- Plugin development properties -->

View File

@@ -13,7 +13,7 @@ namespace BTCPayServer.Plugins.NIP05
{ {
public override IBTCPayServerPlugin.PluginDependency[] Dependencies { get; } = public override IBTCPayServerPlugin.PluginDependency[] Dependencies { get; } =
{ {
new() {Identifier = nameof(BTCPayServer), Condition = ">=1.13.0"} new() {Identifier = nameof(BTCPayServer), Condition = ">=2.0.0"}
}; };
public override void Execute(IServiceCollection applicationBuilder) public override void Execute(IServiceCollection applicationBuilder)

View File

@@ -32,7 +32,7 @@
<div class="col-xl-8 col-xxl-constrain"> <div class="col-xl-8 col-xxl-constrain">
<div class="form-group"> <div class="form-group">
<div class="form-group pt-2"> <div class="form-group pt-2">
<label asp-for="Name (does not need to be same as for a lightning address)" class="form-label"></label> <label asp-for="Name" class="form-label">Name (does not need to be same as for a lightning address)</label>
<div class="input-group"> <div class="input-group">
<input asp-for="Name" class="form-control"/> <input asp-for="Name" class="form-control"/>
<span class="input-group-text">@@@Context.Request.Host.ToUriComponent()@Context.Request.PathBase</span> <span class="input-group-text">@@@Context.Request.Host.ToUriComponent()@Context.Request.PathBase</span>

View File

@@ -5,11 +5,9 @@ using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using BTCPayServer.Events; using BTCPayServer.Events;
using BTCPayServer.Models.InvoicingModels;
using BTCPayServer.Payments; using BTCPayServer.Payments;
using BTCPayServer.Services; using BTCPayServer.Services;
using BTCPayServer.Services.Invoices; using BTCPayServer.Services.Invoices;
using BTCPayServer.Services.Stores;
using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@@ -23,7 +21,8 @@ namespace BTCPayServer.Plugins.NIP05;
public class Zapper : IHostedService public class Zapper : IHostedService
{ {
record PendingZapEvent(string[] relays, NostrEvent nostrEvent); record PendingZapEvent(string[] relays, NostrEvent nostrEvent);
private readonly PaymentMethodHandlerDictionary _paymentMethodHandlerDictionary;
private readonly EventAggregator _eventAggregator; private readonly EventAggregator _eventAggregator;
private readonly Nip5Controller _nip5Controller; private readonly Nip5Controller _nip5Controller;
private readonly IMemoryCache _memoryCache; private readonly IMemoryCache _memoryCache;
@@ -51,7 +50,9 @@ public class Zapper : IHostedService
} }
public Zapper(EventAggregator eventAggregator, public Zapper(
PaymentMethodHandlerDictionary paymentMethodHandlerDictionary,
EventAggregator eventAggregator,
Nip5Controller nip5Controller, Nip5Controller nip5Controller,
IMemoryCache memoryCache, IMemoryCache memoryCache,
ILogger<Zapper> logger, ILogger<Zapper> logger,
@@ -59,6 +60,7 @@ public class Zapper : IHostedService
InvoiceRepository invoiceRepository, InvoiceRepository invoiceRepository,
NostrClientPool nostrClientPool) NostrClientPool nostrClientPool)
{ {
_paymentMethodHandlerDictionary = paymentMethodHandlerDictionary;
_eventAggregator = eventAggregator; _eventAggregator = eventAggregator;
_nip5Controller = nip5Controller; _nip5Controller = nip5Controller;
_memoryCache = memoryCache; _memoryCache = memoryCache;
@@ -150,7 +152,8 @@ public class Zapper : IHostedService
{ {
if (arg.EventCode != InvoiceEventCode.Completed && arg.EventCode != InvoiceEventCode.MarkedCompleted) if (arg.EventCode != InvoiceEventCode.Completed && arg.EventCode != InvoiceEventCode.MarkedCompleted)
return; return;
var pm = arg.Invoice.GetPaymentMethod(new PaymentMethodId("BTC", LNURLPayPaymentType.Instance)); var pmi = PaymentTypes.LNURL.GetPaymentMethodId("BTC");
var pm = arg.Invoice.GetPaymentPrompt(pmi);
if (pm is null) if (pm is null)
{ {
return; return;
@@ -159,8 +162,7 @@ public class Zapper : IHostedService
{ {
return; return;
} }
var pmd = (LNURLPayPaymentMethodDetails) pm.GetPaymentMethodDetails();
var settings = await GetSettings(); var settings = await GetSettings();
var zapRequestEvent = JsonSerializer.Deserialize<NostrEvent>(zapRequest); var zapRequestEvent = JsonSerializer.Deserialize<NostrEvent>(zapRequest);
@@ -168,13 +170,13 @@ public class Zapper : IHostedService
var tags = zapRequestEvent.Tags.Where(a => a.TagIdentifier.Length == 1).ToList(); var tags = zapRequestEvent.Tags.Where(a => a.TagIdentifier.Length == 1).ToList();
tags.AddRange(new[] tags.AddRange(new[]
{ {
new NostrEventTag new NostrEventTag
{ {
TagIdentifier = "bolt11", TagIdentifier = "bolt11",
Data = new() {pmd.BOLT11} Data = new() {pm.Destination}
}, },
new NostrEventTag() new NostrEventTag()

View File

@@ -11,7 +11,7 @@
<PropertyGroup> <PropertyGroup>
<Product>Prism</Product> <Product>Prism</Product>
<Description>Automated value splits for Bitcoin.</Description> <Description>Automated value splits for Bitcoin.</Description>
<Version>1.2.8</Version> <Version>1.2.9</Version>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup> </PropertyGroup>
<!-- Plugin development properties --> <!-- Plugin development properties -->

View File

@@ -4,6 +4,7 @@
@using BTCPayServer.HostedServices @using BTCPayServer.HostedServices
@using BTCPayServer.Payments @using BTCPayServer.Payments
@using BTCPayServer.PayoutProcessors @using BTCPayServer.PayoutProcessors
@using BTCPayServer.Payouts
@using Microsoft.AspNetCore.Http @using Microsoft.AspNetCore.Http
@using Microsoft.AspNetCore.Routing @using Microsoft.AspNetCore.Routing
@using Microsoft.Extensions.Logging @using Microsoft.Extensions.Logging
@@ -28,7 +29,7 @@ else
{ {
<datalist id="users"> <datalist id="users">
<option value="*">Catch-all lightning payments made against invoices in your store (excluding when other prisms are configured that capture those payments.)</option> <option value="*">Catch-all lightning payments made against invoices in your store (excluding when other prisms are configured that capture those payments.)</option>
<option value="*@BitcoinPaymentType.Instance.ToStringNormalized()">Catch-all on-chain payments made against invoices in your store</option> <option value="*@PaymentTypes.CHAIN.ToString()">Catch-all on-chain payments made against invoices in your store</option>
<option value="*All">Catch-all any payments made against invoices in your store</option> <option value="*All">Catch-all any payments made against invoices in your store</option>
@foreach (var user in Users) @foreach (var user in Users)
@@ -201,8 +202,8 @@ else
public bool Loading { get; set; } = true; public bool Loading { get; set; } = true;
public List<LightningAddressData> Users { get; set; } = new(); public List<LightningAddressData> Users { get; set; } = new();
public PaymentMethodId pmi { get; set; } = new("BTC", LightningPaymentType.Instance); public PayoutMethodId pmi { get; set; } = PayoutTypes.LN.GetPayoutMethodId("BTC");
public PaymentMethodId pmichain { get; set; } = new("BTC", PaymentTypes.BTCLike); public PayoutMethodId pmichain { get; set; } = PayoutTypes.CHAIN.GetPayoutMethodId("BTC");
public bool NoPayoutProcessors { get; set; } public bool NoPayoutProcessors { get; set; }
private string PrismEditButtonsFilter { get; set; } private string PrismEditButtonsFilter { get; set; }
@@ -224,7 +225,7 @@ else
var fetchProcessors = PayoutProcessorService.GetProcessors(new PayoutProcessorService.PayoutProcessorQuery() var fetchProcessors = PayoutProcessorService.GetProcessors(new PayoutProcessorService.PayoutProcessorQuery()
{ {
Stores = new[] {StoreId}, Stores = new[] {StoreId},
PaymentMethods = new[] {pmi.ToString(), pmichain.ToString()} PayoutMethods = new[] {pmi, pmichain}
}); });
var tasks = new Task[] var tasks = new Task[]
@@ -243,8 +244,8 @@ else
EditContext.OnFieldChanged += FieldChanged; EditContext.OnFieldChanged += FieldChanged;
SatBreaker.PrismUpdated += SatBreakerOnPrismUpdated; SatBreaker.PrismUpdated += SatBreakerOnPrismUpdated;
//set NoPayoutProcessors to true if there are no configured payout processores for pmi and pmichain //set NoPayoutProcessors to true if there are no configured payout processores for pmi and pmichain
NoPayoutProcessors = PayoutProcessorFactories.Any(factory => factory.GetSupportedPaymentMethods().Contains(pmi)) && (await fetchProcessors).All(data => NoPayoutProcessors = PayoutProcessorFactories.Any(factory => factory.GetSupportedPayoutMethods().Contains(pmi)) && (await fetchProcessors).All(data =>
!new[] {pmi, pmichain}.Contains(data.GetPaymentMethodId())); !new[] {pmi, pmichain}.Contains(data.GetPayoutMethodId()));
Loading = false; Loading = false;
await InvokeAsync(StateHasChanged); await InvokeAsync(StateHasChanged);

View File

@@ -2,6 +2,7 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using BTCPayServer.Abstractions.Contracts; using BTCPayServer.Abstractions.Contracts;
using BTCPayServer.Payments; using BTCPayServer.Payments;
using BTCPayServer.Payouts;
namespace BTCPayServer.Plugins.Prism; namespace BTCPayServer.Plugins.Prism;
@@ -19,7 +20,7 @@ public class LNURLPrismDestinationValidator : IPluginHookFilter
return Task.FromResult<object>(new PrismDestinationValidationResult() return Task.FromResult<object>(new PrismDestinationValidationResult()
{ {
Success = true, Success = true,
PaymentMethod = new PaymentMethodId("BTC", PaymentTypes.LNURLPay) PayoutMethodId = PayoutTypes.LN.GetPayoutMethodId("BTC")
}); });
} }
catch (Exception e) catch (Exception e)
@@ -30,7 +31,7 @@ public class LNURLPrismDestinationValidator : IPluginHookFilter
return Task.FromResult<object>(new PrismDestinationValidationResult() return Task.FromResult<object>(new PrismDestinationValidationResult()
{ {
Success = true, Success = true,
PaymentMethod = new PaymentMethodId("BTC", PaymentTypes.LNURLPay) PayoutMethodId =PayoutTypes.LN.GetPayoutMethodId("BTC")
}); });
} }
catch (Exception) catch (Exception)
@@ -45,5 +46,5 @@ public class LNURLPrismDestinationValidator : IPluginHookFilter
public class PrismDestinationValidationResult public class PrismDestinationValidationResult
{ {
public bool Success { get; set; } public bool Success { get; set; }
public PaymentMethodId PaymentMethod { get; set; } public PayoutMethodId PayoutMethodId { get; set; }
} }

View File

@@ -1,10 +1,9 @@
using System; using System;
using System.Collections;
using System.Threading.Tasks; using System.Threading.Tasks;
using BTCPayServer.Abstractions.Contracts; using BTCPayServer.Abstractions.Contracts;
using BTCPayServer.Data; using BTCPayServer.Data;
using BTCPayServer.HostedServices; using BTCPayServer.HostedServices;
using BTCPayServer.Payments; using BTCPayServer.Payouts;
using NBitcoin; using NBitcoin;
using NBXplorer.DerivationStrategy; using NBXplorer.DerivationStrategy;
@@ -35,7 +34,7 @@ public class OnChainPrismClaimCreate : IPluginHookFilter
{ {
claimRequest.Destination = claimRequest.Destination =
new AddressClaimDestination(BitcoinAddress.Create(destStr, network.NBitcoinNetwork)); new AddressClaimDestination(BitcoinAddress.Create(destStr, network.NBitcoinNetwork));
claimRequest.PaymentMethodId = new PaymentMethodId("BTC", BitcoinPaymentType.Instance); claimRequest.PayoutMethodId = PayoutTypes.CHAIN.GetPayoutMethodId("BTC");
return args; return args;
} }
catch (Exception) catch (Exception)
@@ -48,7 +47,7 @@ public class OnChainPrismClaimCreate : IPluginHookFilter
claimRequest.Destination = claimRequest.Destination =
new AddressClaimDestination(add.Address); new AddressClaimDestination(add.Address);
claimRequest.PaymentMethodId = new PaymentMethodId("BTC", BitcoinPaymentType.Instance); claimRequest.PayoutMethodId = PayoutTypes.CHAIN.GetPayoutMethodId("BTC");
} }
catch (Exception exception) catch (Exception exception)
{ {

View File

@@ -3,6 +3,7 @@ using System.Threading.Tasks;
using BTCPayServer.Abstractions.Contracts; using BTCPayServer.Abstractions.Contracts;
using BTCPayServer.Data; using BTCPayServer.Data;
using BTCPayServer.Payments; using BTCPayServer.Payments;
using BTCPayServer.Payouts;
using NBitcoin; using NBitcoin;
using NBXplorer; using NBXplorer;
@@ -34,7 +35,7 @@ public class OnChainPrismDestinationValidator : IPluginHookFilter
return Task.FromResult<object>(new PrismDestinationValidationResult() return Task.FromResult<object>(new PrismDestinationValidationResult()
{ {
Success = true, Success = true,
PaymentMethod = new PaymentMethodId("BTC", PaymentTypes.BTCLike) PayoutMethodId = PayoutTypes.CHAIN.GetPayoutMethodId("BTC")
}); });
} }
catch (Exception e) catch (Exception e)
@@ -46,7 +47,7 @@ public class OnChainPrismDestinationValidator : IPluginHookFilter
return Task.FromResult<object>(new PrismDestinationValidationResult() return Task.FromResult<object>(new PrismDestinationValidationResult()
{ {
Success = true, Success = true,
PaymentMethod = new PaymentMethodId("BTC", PaymentTypes.BTCLike) PayoutMethodId =PayoutTypes.CHAIN.GetPayoutMethodId("BTC")
}); });
} }
catch (Exception) catch (Exception)

View File

@@ -5,6 +5,7 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using BTCPayServer.Abstractions.Contracts; using BTCPayServer.Abstractions.Contracts;
using BTCPayServer.Payments; using BTCPayServer.Payments;
using BTCPayServer.Payouts;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
@@ -30,7 +31,7 @@ public class OpenSatsDestinationValidator : IPluginHookFilter
var parts = args1.ToLowerInvariant().Split(":", StringSplitOptions.RemoveEmptyEntries); var parts = args1.ToLowerInvariant().Split(":", StringSplitOptions.RemoveEmptyEntries);
var project = "general_fund"; var project = "general_fund";
var paymentMethod = new PaymentMethodId("BTC", PaymentTypes.LightningLike); var paymentMethod = PayoutTypes.LN.GetPayoutMethodId("BTC");
if (parts.Length > 1) if (parts.Length > 1)
{ {
project = parts[1]; project = parts[1];
@@ -38,12 +39,11 @@ public class OpenSatsDestinationValidator : IPluginHookFilter
if (parts.Length > 2) if (parts.Length > 2)
{ {
paymentMethod = PaymentMethodId.Parse(parts[2]); paymentMethod = PayoutMethodId.Parse(parts[2]);
} }
var handler = _serviceProvider.GetServices<IPayoutHandler>().FindPayoutHandler(paymentMethod); if (_serviceProvider.GetService<PayoutMethodHandlerDictionary>().TryGetValue(paymentMethod, out var handler))
if (handler is null)
{ {
result.Success = false; result.Success = false;
} }
@@ -63,7 +63,7 @@ public class OpenSatsDestinationValidator : IPluginHookFilter
var invoiceBtcpayModel = JObject.Parse(await httpClient.GetStringAsync(invoiceUrl).ConfigureAwait(false)); var invoiceBtcpayModel = JObject.Parse(await httpClient.GetStringAsync(invoiceUrl).ConfigureAwait(false));
var destination = invoiceBtcpayModel.Value<string>("btcAddress"); var destination = invoiceBtcpayModel.Value<string>("btcAddress");
var claimDestination = await handler.ParseClaimDestination(paymentMethod,destination, CancellationToken.None); var claimDestination = await handler.ParseClaimDestination(destination, CancellationToken.None);
if (claimDestination.destination is null) if (claimDestination.destination is null)
{ {
@@ -72,7 +72,7 @@ public class OpenSatsDestinationValidator : IPluginHookFilter
result.Success = true; result.Success = true;
result.PaymentMethod = paymentMethod; result.PayoutMethodId = paymentMethod;
return result; return result;
} }
catch (Exception e) catch (Exception e)

View File

@@ -1,18 +1,12 @@
using System; using System;
using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using BTCPayServer.Abstractions.Contracts; using BTCPayServer.Abstractions.Contracts;
using BTCPayServer.Abstractions.Custodians;
using BTCPayServer.Abstractions.Extensions;
using BTCPayServer.Data;
using BTCPayServer.HostedServices; using BTCPayServer.HostedServices;
using BTCPayServer.Payments; using BTCPayServer.Payouts;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using NBitcoin;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
namespace BTCPayServer.Plugins.Prism; namespace BTCPayServer.Plugins.Prism;
@@ -38,10 +32,10 @@ public class OpenSatsPrismClaimCreate : IPluginHookFilter
try try
{ {
var parts = args1.Split(":", StringSplitOptions.RemoveEmptyEntries); var parts = args1.Split(":", StringSplitOptions.RemoveEmptyEntries);
var project = "opensats"; var project = "opensats";
var paymentMethod = new PaymentMethodId("BTC", PaymentTypes.LightningLike); var paymentMethod = PayoutTypes.LN.GetPayoutMethodId("BTC");
if (parts.Length > 1) if (parts.Length > 1)
{ {
project = parts[1]; project = parts[1];
@@ -49,11 +43,11 @@ public class OpenSatsPrismClaimCreate : IPluginHookFilter
if (parts.Length > 2) if (parts.Length > 2)
{ {
paymentMethod = PaymentMethodId.Parse(parts[2]); paymentMethod = PayoutMethodId.Parse(parts[2]);
} }
var handler = _serviceProvider.GetServices<IPayoutHandler>().FindPayoutHandler(paymentMethod); _serviceProvider.GetService<PayoutMethodHandlerDictionary>().TryGetValue(paymentMethod, out var handler);
if (handler is null) if (handler is null)
{ {
return null; return null;
@@ -75,7 +69,7 @@ public class OpenSatsPrismClaimCreate : IPluginHookFilter
var destination = invoiceBtcpayModel.Value<string>("btcAddress"); var destination = invoiceBtcpayModel.Value<string>("btcAddress");
var receiptLink = invoiceBtcpayModel.Value<string>("receiptLink"); var receiptLink = invoiceBtcpayModel.Value<string>("receiptLink");
var claimDestination = await handler.ParseClaimDestination(paymentMethod,destination, CancellationToken.None); var claimDestination = await handler.ParseClaimDestination(destination, CancellationToken.None);
if (claimDestination.destination is null) if (claimDestination.destination is null)
{ {
@@ -88,7 +82,7 @@ public class OpenSatsPrismClaimCreate : IPluginHookFilter
}); });
claimRequest.Destination = claimDestination.destination; claimRequest.Destination = claimDestination.destination;
claimRequest.PaymentMethodId = paymentMethod; claimRequest.PayoutMethodId = paymentMethod;
return claimRequest; return claimRequest;

View File

@@ -11,7 +11,7 @@ public class PrismPlugin : BaseBTCPayServerPlugin
{ {
public override IBTCPayServerPlugin.PluginDependency[] Dependencies { get; } = public override IBTCPayServerPlugin.PluginDependency[] Dependencies { get; } =
{ {
new() {Identifier = nameof(BTCPayServer), Condition = ">=1.13.0"} new() {Identifier = nameof(BTCPayServer), Condition = ">=2.0.0"}
}; };
public override void Execute(IServiceCollection applicationBuilder) public override void Execute(IServiceCollection applicationBuilder)

View File

@@ -20,5 +20,5 @@ public class PrismDestination
public string Destination { get; set; } public string Destination { get; set; }
public decimal? Reserve { get; set; } public decimal? Reserve { get; set; }
public long? SatThreshold { get; set; } public long? SatThreshold { get; set; }
public string? PaymentMethodId { get; set; } public string? PayoutMethodId { get; set; }
} }

View File

@@ -13,6 +13,7 @@ using BTCPayServer.HostedServices;
using BTCPayServer.Lightning; using BTCPayServer.Lightning;
using BTCPayServer.Payments; using BTCPayServer.Payments;
using BTCPayServer.Payments.Lightning; using BTCPayServer.Payments.Lightning;
using BTCPayServer.Payouts;
using BTCPayServer.Services; using BTCPayServer.Services;
using BTCPayServer.Services.Invoices; using BTCPayServer.Services.Invoices;
using BTCPayServer.Services.Stores; using BTCPayServer.Services.Stores;
@@ -47,13 +48,13 @@ namespace BTCPayServer.Plugins.Prism
{ {
private readonly StoreRepository _storeRepository; private readonly StoreRepository _storeRepository;
private readonly ILogger<SatBreaker> _logger; private readonly ILogger<SatBreaker> _logger;
private readonly LightningAddressService _lightningAddressService;
private readonly PullPaymentHostedService _pullPaymentHostedService; private readonly PullPaymentHostedService _pullPaymentHostedService;
private readonly LightningLikePayoutHandler _lightningLikePayoutHandler;
private readonly BTCPayNetworkProvider _btcPayNetworkProvider; private readonly BTCPayNetworkProvider _btcPayNetworkProvider;
private readonly LightningClientFactoryService _lightningClientFactoryService; private readonly LightningClientFactoryService _lightningClientFactoryService;
private readonly IOptions<LightningNetworkOptions> _lightningNetworkOptions; private readonly IOptions<LightningNetworkOptions> _lightningNetworkOptions;
private readonly BTCPayNetworkJsonSerializerSettings _btcPayNetworkJsonSerializerSettings; private readonly BTCPayNetworkJsonSerializerSettings _btcPayNetworkJsonSerializerSettings;
private readonly PaymentMethodHandlerDictionary _paymentMethodHandlerDictionary;
private readonly PayoutMethodHandlerDictionary _payoutMethodHandlerDictionary;
private readonly IPluginHookService _pluginHookService; private readonly IPluginHookService _pluginHookService;
private Dictionary<string, PrismSettings> _prismSettings; private Dictionary<string, PrismSettings> _prismSettings;
@@ -62,24 +63,24 @@ namespace BTCPayServer.Plugins.Prism
public SatBreaker(StoreRepository storeRepository, public SatBreaker(StoreRepository storeRepository,
EventAggregator eventAggregator, EventAggregator eventAggregator,
ILogger<SatBreaker> logger, ILogger<SatBreaker> logger,
LightningAddressService lightningAddressService,
PullPaymentHostedService pullPaymentHostedService, PullPaymentHostedService pullPaymentHostedService,
LightningLikePayoutHandler lightningLikePayoutHandler,
BTCPayNetworkProvider btcPayNetworkProvider, BTCPayNetworkProvider btcPayNetworkProvider,
LightningClientFactoryService lightningClientFactoryService, LightningClientFactoryService lightningClientFactoryService,
IOptions<LightningNetworkOptions> lightningNetworkOptions, IOptions<LightningNetworkOptions> lightningNetworkOptions,
BTCPayNetworkJsonSerializerSettings btcPayNetworkJsonSerializerSettings, BTCPayNetworkJsonSerializerSettings btcPayNetworkJsonSerializerSettings,
PaymentMethodHandlerDictionary paymentMethodHandlerDictionary,
PayoutMethodHandlerDictionary payoutMethodHandlerDictionary,
IPluginHookService pluginHookService) : base(eventAggregator, logger) IPluginHookService pluginHookService) : base(eventAggregator, logger)
{ {
_storeRepository = storeRepository; _storeRepository = storeRepository;
_logger = logger; _logger = logger;
_lightningAddressService = lightningAddressService;
_pullPaymentHostedService = pullPaymentHostedService; _pullPaymentHostedService = pullPaymentHostedService;
_lightningLikePayoutHandler = lightningLikePayoutHandler;
_btcPayNetworkProvider = btcPayNetworkProvider; _btcPayNetworkProvider = btcPayNetworkProvider;
_lightningClientFactoryService = lightningClientFactoryService; _lightningClientFactoryService = lightningClientFactoryService;
_lightningNetworkOptions = lightningNetworkOptions; _lightningNetworkOptions = lightningNetworkOptions;
_btcPayNetworkJsonSerializerSettings = btcPayNetworkJsonSerializerSettings; _btcPayNetworkJsonSerializerSettings = btcPayNetworkJsonSerializerSettings;
_paymentMethodHandlerDictionary = paymentMethodHandlerDictionary;
_payoutMethodHandlerDictionary = payoutMethodHandlerDictionary;
_pluginHookService = pluginHookService; _pluginHookService = pluginHookService;
} }
@@ -145,20 +146,28 @@ namespace BTCPayServer.Plugins.Prism
{ {
continue; continue;
} }
foreach (var payout in storePayouts) foreach (var payout in storePayouts)
{ {
if (!pendingPayouts.TryGetValue(payout.Id, out var pendingPayout)) if (!pendingPayouts.TryGetValue(payout.Id, out var pendingPayout))
{ {
continue; continue;
} }
if(payout.GetPayoutMethodId() is not { } payoutMethodId)
continue;
if (!_payoutMethodHandlerDictionary.TryGetValue(payoutMethodId, out var handler))
{
continue;
}
long toCredit = 0; long toCredit = 0;
switch (payout.State) switch (payout.State)
{ {
case PayoutState.Completed: case PayoutState.Completed:
var proof = _lightningLikePayoutHandler.ParseProof(payout) as PayoutLightningBlob; var proof = handler.ParseProof(payout) as PayoutLightningBlob;
long? feePaid = null; long? feePaid = null;
if (!string.IsNullOrEmpty(proof?.PaymentHash)) if (!string.IsNullOrEmpty(proof?.PaymentHash))
@@ -168,10 +177,10 @@ namespace BTCPayServer.Plugins.Prism
var store = await _storeRepository.FindStore(payout.StoreDataId); var store = await _storeRepository.FindStore(payout.StoreDataId);
var network = _btcPayNetworkProvider.GetNetwork<BTCPayNetwork>("BTC"); var network = _btcPayNetworkProvider.GetNetwork<BTCPayNetwork>("BTC");
var id = new PaymentMethodId("BTC", LightningPaymentType.Instance); var id = PaymentTypes.LN.GetPaymentMethodId("BTC");
var existing = store.GetSupportedPaymentMethods(_btcPayNetworkProvider) var existing =
.OfType<LightningSupportedPaymentMethod>() store.GetPaymentMethodConfig<LightningPaymentMethodConfig>(id,
.FirstOrDefault(d => d.PaymentId == id); _paymentMethodHandlerDictionary);
if (existing?.GetExternalLightningUrl() is { } connectionString) if (existing?.GetExternalLightningUrl() is { } connectionString)
{ {
lnClient = _lightningClientFactoryService.Create(connectionString, lnClient = _lightningClientFactoryService.Create(connectionString,
@@ -313,29 +322,34 @@ namespace BTCPayServer.Plugins.Prism
private (Split, LightMoney)[] DetermineMatches(PrismSettings prismSettings, InvoiceEntity entity) private (Split, LightMoney)[] DetermineMatches(PrismSettings prismSettings, InvoiceEntity entity)
{ {
//first check the primary thing - ln address //first check the primary thing - ln address
var explicitPMI = new PaymentMethodId("BTC", LNURLPayPaymentType.Instance); var explicitPMI = PaymentTypes.LNURL.GetPaymentMethodId("BTC");
var pm = entity.GetPaymentMethod(explicitPMI); var pm = entity.GetPaymentPrompt(explicitPMI);
var pmd = pm?.GetPaymentMethodDetails() as LNURLPayPaymentMethodDetails;
List<(Split, LightMoney)> result = new();
var payments = entity.GetPayments(true).GroupBy(paymentEntity => paymentEntity.GetPaymentMethodId()).ToArray(); var payments = entity.GetPayments(true).GroupBy(paymentEntity => paymentEntity.PaymentMethodId).ToArray();
if (pmd?.ConsumedLightningAddress is not null) List<(Split, LightMoney)> result = new();
if(_paymentMethodHandlerDictionary.TryGetValue(explicitPMI, out var handler) && pm is not null)
{ {
var address = pmd.ConsumedLightningAddress.Split("@")[0]; var pmd = handler.ParsePaymentPromptDetails(pm.Details) as LNURLPayPaymentMethodDetails;
var matchedExplicit = prismSettings.Splits.FirstOrDefault(s =>
s.Source.Equals(address, StringComparison.InvariantCultureIgnoreCase));
if (pmd?.ConsumedLightningAddress is not null)
if (matchedExplicit is not null)
{ {
var explicitPayments = payments.FirstOrDefault(grouping => var address = pmd.ConsumedLightningAddress.Split("@")[0];
grouping.Key == explicitPMI)?.Sum(paymentEntity => paymentEntity.PaidAmount.Net); var matchedExplicit = prismSettings.Splits.FirstOrDefault(s =>
payments = payments.Where(grouping => grouping.Key != explicitPMI).ToArray(); s.Source.Equals(address, StringComparison.InvariantCultureIgnoreCase));
if (explicitPayments > 0) if (matchedExplicit is not null)
{ {
result.Add((matchedExplicit, LightMoney.FromUnit(explicitPayments.Value, LightMoneyUnit.BTC))); var explicitPayments = payments.FirstOrDefault(grouping =>
} grouping.Key == explicitPMI)?.Sum(paymentEntity => paymentEntity.PaidAmount.Net);
} payments = payments.Where(grouping => grouping.Key != explicitPMI).ToArray();
if (explicitPayments > 0)
{
result.Add((matchedExplicit, LightMoney.FromUnit(explicitPayments.Value, LightMoneyUnit.BTC)));
}
}
}
} }
var catchAlls = prismSettings.Splits.Where(split => split.Source.StartsWith("*")).Select(split => var catchAlls = prismSettings.Splits.Where(split => split.Source.StartsWith("*")).Select(split =>
@@ -346,20 +360,25 @@ namespace BTCPayServer.Plugins.Prism
switch (split.Source) switch (split.Source)
{ {
case "*": case "*":
pmi = new PaymentMethodId("BTC", PaymentTypes.LightningLike); pmi = PaymentTypes.LN.GetPaymentMethodId("BTC");
break; break;
case "*All": case "*All":
break; break;
case var s when PaymentTypes.TryParse(s.Substring(1), out var pType): case var s when s.StartsWith("*") && s.Substring(1) ==PaymentTypes.CHAIN.ToString():
pmi = PaymentTypes.CHAIN.GetPaymentMethodId("BTC");
pmi = new PaymentMethodId("BTC", pType); break;
case var s2 when s2.StartsWith("*") && s2.Substring(1) ==PaymentTypes.LN.ToString():
pmi = PaymentTypes.LN.GetPaymentMethodId("BTC");
break;
case var s3 when s3.StartsWith("*") && s3.Substring(1) ==PaymentTypes.LNURL.ToString():
pmi = PaymentTypes.LNURL.GetPaymentMethodId("BTC");
break; break;
case var s when !PaymentMethodId.TryParse(s.Substring(1), out pmi): case var s when !PaymentMethodId.TryParse(s.Substring(1), out pmi):
valid = false; valid = false;
break; break;
} }
if (pmi is not null && pmi.CryptoCode != "BTC") if (pmi is not null && !pmi.ToString().StartsWith("BTC-"))
{ {
valid = false; valid = false;
} }
@@ -367,6 +386,7 @@ namespace BTCPayServer.Plugins.Prism
return (pmi, valid, split); return (pmi, valid, split);
}).Where(tuple => tuple.valid).ToDictionary(split => split.pmi, split => split.split); }).Where(tuple => tuple.valid).ToDictionary(split => split.pmi, split => split.split);
while(payments.Any() || catchAlls.Any()) while(payments.Any() || catchAlls.Any())
{ {
decimal paymentSum; decimal paymentSum;
@@ -511,9 +531,9 @@ namespace BTCPayServer.Plugins.Prism
continue; continue;
} }
var pmi = string.IsNullOrEmpty(destinationSettings?.PaymentMethodId) || var pmi = string.IsNullOrEmpty(destinationSettings?.PayoutMethodId) ||
!PaymentMethodId.TryParse(destinationSettings?.PaymentMethodId, out var pmi2) !PayoutMethodId.TryParse(destinationSettings?.PayoutMethodId, out var pmi2)
? new PaymentMethodId("BTC", LightningPaymentType.Instance) ? PayoutTypes.LN.GetPayoutMethodId("BTC")
: pmi2; : pmi2;
var source = "Prism"; var source = "Prism";
@@ -526,7 +546,7 @@ namespace BTCPayServer.Plugins.Prism
Destination = new PrismPlaceholderClaimDestination(destinationSettings?.Destination ?? destination), Destination = new PrismPlaceholderClaimDestination(destinationSettings?.Destination ?? destination),
PreApprove = true, PreApprove = true,
StoreId = storeId, StoreId = storeId,
PaymentMethodId = pmi, PayoutMethodId = pmi,
Value = Money.Satoshis(payoutAmount).ToDecimal(MoneyUnit.BTC), Value = Money.Satoshis(payoutAmount).ToDecimal(MoneyUnit.BTC),
Metadata = JObject.FromObject(new Metadata = JObject.FromObject(new
{ {

View File

@@ -9,7 +9,7 @@
<PropertyGroup> <PropertyGroup>
<Product>SideShift</Product> <Product>SideShift</Product>
<Description>Allows you to embed a SideShift conversion screen to allow customers to pay with altcoins.</Description> <Description>Allows you to embed a SideShift conversion screen to allow customers to pay with altcoins.</Description>
<Version>1.1.13</Version> <Version>1.1.14</Version>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup> </PropertyGroup>
<!-- Plugin development properties --> <!-- Plugin development properties -->

View File

@@ -1,84 +0,0 @@
using System;
using System.Net.Http;
using System.Threading.Tasks;
using BTCPayServer.Abstractions.Contracts;
using BTCPayServer.Data.Payouts.LightningLike;
using BTCPayServer.HostedServices;
using BTCPayServer.Lightning;
using Newtonsoft.Json.Linq;
namespace BTCPayServer.Plugins.SideShift;
public class PrismClaimCreate : IPluginHookFilter
{
private readonly IHttpClientFactory _httpClientFactory;
private readonly BTCPayNetworkProvider _networkProvider;
public string Hook => "prism-claim-create";
public PrismClaimCreate(IHttpClientFactory httpClientFactory, BTCPayNetworkProvider networkProvider)
{
_httpClientFactory = httpClientFactory;
_networkProvider = networkProvider;
}
public async Task<object> Execute(object args)
{
var network = _networkProvider.GetNetwork<BTCPayNetwork>("BTC");
if (args is not ClaimRequest claimRequest || network is null)
{
return args;
}
if (claimRequest.Destination?.ToString() is not { } args1 || !args1.StartsWith("sideshift:")) return args;
var request = JObject.Parse(args1.Substring("sideshift:".Length)).ToObject<PrismSideshiftDestination>();
if (!request.Valid())
{
return null;
}
var client = _httpClientFactory.CreateClient("sideshift");
var shiftResponse = await client.PostAsJsonAsync("https://sideshift.ai/api/v2/shifts/variable", new
{
settleAddress = request.ShiftDestination,
affiliateId = "qg0OrfHJV",
settleMemo = request.ShiftMemo,
depositCoin = "BTC",
depositNetwork = request.SourceNetwork?? "lightning",
settleCoin = request.ShiftCoin,
settleNetwork = request.ShiftNetwork,
}
);
if (!shiftResponse.IsSuccessStatusCode)
{
return null;
}
var shift = await shiftResponse.Content.ReadAsAsync<SideShiftController.ShiftResponse>();
try
{
LNURL.LNURL.Parse(shift.depositAddress, out _);
claimRequest.Destination = new LNURLPayClaimDestinaton(shift.depositAddress);
claimRequest.Metadata = JObject.FromObject(new
{
Source = $"Prism->Sideshift",
SourceLink = $"https://sideshift.ai/orders/{shift.id}?openSupport=true",
});
return claimRequest;
}
catch (Exception e)
{
if (BOLT11PaymentRequest.TryParse(shift.depositAddress, out var bolt11, network.NBitcoinNetwork))
{
claimRequest.Destination = new BoltInvoiceClaimDestination(shift.depositAddress, bolt11);
claimRequest.Metadata = JObject.FromObject(new
{
Source = $"Prism->Sideshift",
SourceLink = $"https://sideshift.ai/orders/{shift.id}?openSupport=true",
});
return claimRequest;
}
}
return null;
}
}

View File

@@ -1,17 +0,0 @@
using System.Threading.Tasks;
using BTCPayServer.Abstractions.Contracts;
using Newtonsoft.Json.Linq;
namespace BTCPayServer.Plugins.SideShift;
public class PrismDestinationValidate : IPluginHookFilter
{
public string Hook => "prism-destination-validate";
public async Task<object> Execute(object args)
{
if (args is not string args1 || !args1.StartsWith("sideshift:")) return args;
var json = JObject.Parse(args1.Substring("sideshift:".Length)).ToObject<PrismSideshiftDestination>();
return json.Valid();
}
}

View File

@@ -1,15 +0,0 @@
using System.Threading.Tasks;
using BTCPayServer.Abstractions.Contracts;
namespace BTCPayServer.Plugins.SideShift;
public class PrismEditFilter : IPluginHookFilter
{
public string Hook => "prism-edit-buttons";
public Task<object> Execute(object args)
{
return Task.FromResult<object>(( args??"") + "<button type='button' class=\"btn btn-primary\" data-bs-toggle=\"modal\" data-bs-target=\"#sideshiftModal\">Generate SideShift destination</button>");
}
}

View File

@@ -1,16 +0,0 @@
namespace BTCPayServer.Plugins.SideShift;
public class PrismSideshiftDestination
{
public string ShiftCoin { get; set; }
public string ShiftNetwork { get; set; }
public string ShiftDestination { get; set; }
public string ShiftMemo { get; set; }
public string SourceNetwork { get; set; }
public bool Valid()
{
return !string.IsNullOrEmpty(ShiftCoin) && !string.IsNullOrEmpty(ShiftNetwork) &&
!string.IsNullOrEmpty(ShiftDestination);
}
}

View File

@@ -9,6 +9,7 @@ using BTCPayServer.Client.Models;
using BTCPayServer.Data; using BTCPayServer.Data;
using BTCPayServer.HostedServices; using BTCPayServer.HostedServices;
using BTCPayServer.Payments; using BTCPayServer.Payments;
using BTCPayServer.Payouts;
using BTCPayServer.Services; using BTCPayServer.Services;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
@@ -26,7 +27,7 @@ namespace BTCPayServer.Plugins.SideShift
{ {
private readonly SideShiftService _sideShiftService; private readonly SideShiftService _sideShiftService;
private readonly IHttpClientFactory _httpClientFactory; private readonly IHttpClientFactory _httpClientFactory;
private readonly IEnumerable<IPayoutHandler> _payoutHandlers; private readonly PayoutMethodHandlerDictionary _payoutMethodHandlerDictionary;
private readonly PullPaymentHostedService _pullPaymentHostedService; private readonly PullPaymentHostedService _pullPaymentHostedService;
private readonly BTCPayNetworkJsonSerializerSettings _serializerSettings; private readonly BTCPayNetworkJsonSerializerSettings _serializerSettings;
private readonly ApplicationDbContextFactory _dbContextFactory; private readonly ApplicationDbContextFactory _dbContextFactory;
@@ -34,13 +35,13 @@ namespace BTCPayServer.Plugins.SideShift
public SideShiftController( public SideShiftController(
SideShiftService sideShiftService, SideShiftService sideShiftService,
IHttpClientFactory httpClientFactory, IHttpClientFactory httpClientFactory,
IEnumerable<IPayoutHandler> payoutHandlers, PayoutMethodHandlerDictionary payoutMethodHandlerDictionary,
PullPaymentHostedService pullPaymentHostedService, PullPaymentHostedService pullPaymentHostedService,
BTCPayNetworkJsonSerializerSettings serializerSettings, ApplicationDbContextFactory dbContextFactory) BTCPayNetworkJsonSerializerSettings serializerSettings, ApplicationDbContextFactory dbContextFactory)
{ {
_sideShiftService = sideShiftService; _sideShiftService = sideShiftService;
_httpClientFactory = httpClientFactory; _httpClientFactory = httpClientFactory;
_payoutHandlers = payoutHandlers; _payoutMethodHandlerDictionary = payoutMethodHandlerDictionary;
_pullPaymentHostedService = pullPaymentHostedService; _pullPaymentHostedService = pullPaymentHostedService;
_serializerSettings = serializerSettings; _serializerSettings = serializerSettings;
_dbContextFactory = dbContextFactory; _dbContextFactory = dbContextFactory;
@@ -111,19 +112,23 @@ namespace BTCPayServer.Plugins.SideShift
ModelState.AddModelError(nameof(request.Amount), "Amount must be specified"); ModelState.AddModelError(nameof(request.Amount), "Amount must be specified");
} }
if (!PaymentMethodId.TryParse(request.PaymentMethod, out var pmi)) if (!PayoutMethodId.TryParse(request.PayoutMethodId, out var pmi))
{ {
ModelState.AddModelError(nameof(request.PaymentMethod), "Invalid payment method"); ModelState.AddModelError(nameof(request.PayoutMethodId), "Invalid payout method");
} }
else else
{ {
handler = _payoutHandlers.FindPayoutHandler(pmi); if (!_payoutMethodHandlerDictionary.TryGetValue(pmi, out handler))
if (handler == null)
{ {
ModelState.AddModelError(nameof(request.PaymentMethod), "Invalid payment method"); ModelState.AddModelError(nameof(request.PayoutMethodId), "Invalid payment method");
} }
} }
var isLN = pmi.ToString().EndsWith("-" +PayoutTypes.LN.Id);
if (isLN)
{
ModelState.AddModelError(nameof(request.PayoutMethodId), "SideShift does not support Lightning payouts");
}
if (!ModelState.IsValid) if (!ModelState.IsValid)
{ {
return this.CreateValidationError(ModelState); return this.CreateValidationError(ModelState);
@@ -166,13 +171,13 @@ namespace BTCPayServer.Plugins.SideShift
// shiftResponse.EnsureSuccessStatusCode(); // shiftResponse.EnsureSuccessStatusCode();
// var shift = await shiftResponse.Content.ReadAsAsync<ShiftResponse>(); // var shift = await shiftResponse.Content.ReadAsAsync<ShiftResponse>();
var cryptoCode = pmi.ToString().Split('-')[0];
var shiftResponse = await client.PostAsJsonAsync("https://sideshift.ai/api/v2/shifts/variable", new var shiftResponse = await client.PostAsJsonAsync("https://sideshift.ai/api/v2/shifts/variable", new
{ {
settleAddress = request.Destination, settleAddress = request.Destination,
affiliateId = "qg0OrfHJV", affiliateId = "qg0OrfHJV",
settleMemo = request.Memo, settleMemo = request.Memo,
depositCoin = pmi.CryptoCode, depositCoin = cryptoCode,
depositNetwork = pmi.PaymentType == LightningPaymentType.Instance ? "lightning" : null,
settleCoin = request.ShiftCurrency, settleCoin = request.ShiftCurrency,
settleNetwork = request.ShiftNetwork, settleNetwork = request.ShiftNetwork,
} }
@@ -188,20 +193,20 @@ namespace BTCPayServer.Plugins.SideShift
var destination = var destination =
await handler.ParseAndValidateClaimDestination(pmi, shift.depositAddress, ppBlob, await handler.ParseAndValidateClaimDestination(shift.depositAddress, ppBlob,
CancellationToken.None); CancellationToken.None);
var claim = await _pullPaymentHostedService.Claim(new ClaimRequest() var claim = await _pullPaymentHostedService.Claim(new ClaimRequest()
{ {
PullPaymentId = pullPaymentId, PullPaymentId = pullPaymentId,
Destination = destination.destination, Destination = destination.destination,
PaymentMethodId = pmi, PayoutMethodId = pmi,
Value = request.Amount Value = request.Amount
}); });
if (claim.Result == ClaimRequest.ClaimResult.Ok) if (claim.Result == ClaimRequest.ClaimResult.Ok)
{ {
await using var ctx = _dbContextFactory.CreateContext(); await using var ctx = _dbContextFactory.CreateContext();
ppBlob.Description += $"<br/>The payout of {claim.PayoutData.Destination} will be forwarded to SideShift.ai for further conversion. Please go to <a href=\"https://sideshift.ai/orders/{shift.id}?openSupport=true\">the order page</a> for support."; ppBlob.Description += $"<br/>The payout of {destination.destination} will be forwarded to SideShift.ai for further conversion. Please go to <a href=\"https://sideshift.ai/orders/{shift.id}?openSupport=true\">the order page</a> for support.";
pp.SetBlob(ppBlob); pp.SetBlob(ppBlob);
ctx.Attach(pp).State = EntityState.Modified; ctx.Attach(pp).State = EntityState.Modified;
await ctx.SaveChangesAsync(); await ctx.SaveChangesAsync();
@@ -242,19 +247,21 @@ namespace BTCPayServer.Plugins.SideShift
private Client.Models.PayoutData ToModel(Data.PayoutData p) private Client.Models.PayoutData ToModel(Data.PayoutData p)
{ {
var blob = p.GetBlob(_serializerSettings); var blob = p.GetBlob(_serializerSettings);
var model = new Client.Models.PayoutData var model = new Client.Models.PayoutData()
{ {
Id = p.Id, Id = p.Id,
PullPaymentId = p.PullPaymentDataId, PullPaymentId = p.PullPaymentDataId,
Date = p.Date, Date = p.Date,
Amount = blob.Amount, OriginalCurrency = p.OriginalCurrency,
PaymentMethodAmount = blob.CryptoAmount, OriginalAmount = p.OriginalAmount,
PayoutCurrency = p.Currency,
PayoutAmount = p.Amount,
Revision = blob.Revision, Revision = blob.Revision,
State = p.State, State = p.State,
PayoutMethodId = p.PayoutMethodId,
PaymentProof = p.GetProofBlobJson(),
Destination = blob.Destination, Destination = blob.Destination,
PaymentMethod = p.PaymentMethodId, Metadata = blob.Metadata?? new JObject(),
CryptoCode = p.GetPaymentMethodId().CryptoCode,
PaymentProof = p.GetProofBlobJson()
}; };
return model; return model;
} }

View File

@@ -1,6 +1,5 @@
using BTCPayServer.Abstractions.Contracts; using BTCPayServer.Abstractions.Contracts;
using BTCPayServer.Abstractions.Models; using BTCPayServer.Abstractions.Models;
using BTCPayServer.Abstractions.Services;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
namespace BTCPayServer.Plugins.SideShift namespace BTCPayServer.Plugins.SideShift
@@ -8,41 +7,22 @@ namespace BTCPayServer.Plugins.SideShift
public class SideShiftPlugin : BaseBTCPayServerPlugin public class SideShiftPlugin : BaseBTCPayServerPlugin
{ {
public override IBTCPayServerPlugin.PluginDependency[] Dependencies { get; } = public override IBTCPayServerPlugin.PluginDependency[] Dependencies { get; } =
{ { new() { Identifier = nameof(BTCPayServer), Condition = ">=2.0.0" }
new() {Identifier = nameof(BTCPayServer), Condition = ">=1.12.0"}
}; };
public override void Execute(IServiceCollection applicationBuilder) public override void Execute(IServiceCollection applicationBuilder)
{ {
applicationBuilder.AddSingleton<SideShiftService>(); applicationBuilder.AddSingleton<SideShiftService>();
applicationBuilder.AddHostedService(provider => provider.GetService<SideShiftService>()); applicationBuilder.AddHostedService(provider => provider.GetService<SideShiftService>());
applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("SideShift/SideShiftNav",
"store-integrations-nav")); applicationBuilder.AddUIExtension("store-integrations-nav","SideShift/SideShiftNav");
applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("SideShift/PullPaymentViewInsert", applicationBuilder.AddUIExtension("pullpayment-foot","SideShift/PullPaymentViewInsert");
"pullpayment-foot")); applicationBuilder.AddUIExtension("store-integrations-list", "SideShift/StoreIntegrationSideShiftOption");
applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("SideShift/StoreIntegrationSideShiftOption",
"store-integrations-list"));
// Checkout v2 // Checkout v2
applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("SideShift/CheckoutPaymentMethodExtension", applicationBuilder.AddUIExtension("checkout-payment-method", "SideShift/CheckoutPaymentMethodExtension");
"checkout-payment-method")); applicationBuilder.AddUIExtension("checkout-payment","SideShift/CheckoutPaymentExtension");
applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("SideShift/CheckoutPaymentExtension",
"checkout-payment"));
// Checkout Classic
applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("SideShift/CheckoutContentExtension",
"checkout-bitcoin-post-content"));
applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("SideShift/CheckoutContentExtension",
"checkout-lightning-post-content"));
applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("SideShift/CheckoutTabExtension",
"checkout-bitcoin-post-tabs"));
applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("SideShift/CheckoutTabExtension",
"checkout-lightning-post-tabs"));
applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("SideShift/CheckoutEnd",
"checkout-end"));
applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("SideShift/PrismEnhance",
"prism-edit"));
applicationBuilder.AddSingleton<IPluginHookFilter, PrismDestinationValidate>();
applicationBuilder.AddSingleton<IPluginHookFilter, PrismClaimCreate>();
applicationBuilder.AddSingleton<IPluginHookFilter, PrismEditFilter>();
base.Execute(applicationBuilder); base.Execute(applicationBuilder);
} }
} }

View File

@@ -1,26 +0,0 @@
@using BTCPayServer.Plugins.SideShift
@inject SideShiftService SideShiftService
@model BTCPayServer.Models.InvoicingModels.PaymentModel
@{
var settings = await SideShiftService.GetSideShiftForInvoice(Model.InvoiceId, Model.StoreId);
if (settings?.Enabled is true)
{
<div id="sideshift" class="bp-view payment manual-flow" :class="{ active: currentTab == 'undefined' || currentTab == 'sideshift' }">
<div class="manual__step-two__instructions">
<span>
{{$t("ConversionTab_BodyTop", srvModel)}}
<br/><br/>
{{$t("ConversionTab_BodyDesc", srvModel)}}
</span>
</div>
<side-shift inline-template
:to-currency="srvModel.paymentMethodId"
:to-currency-due="srvModel.btcDue * (1 + (@settings.AmountMarkupPercentage / 100)) "
:to-currency-address="srvModel.btcAddress">
<a v-on:click="openDialog($event)" href="#" class="action-button btn btn-secondary rounded-pill w-100 mt-4">{{$t("Pay with SideShift")}}</a>
</side-shift>
</div>
}
}

View File

@@ -1,14 +0,0 @@
@using BTCPayServer.Plugins.SideShift
@inject BTCPayServer.Security.ContentSecurityPolicies csp
@inject SideShiftService SideShiftService
@model BTCPayServer.Models.InvoicingModels.PaymentModel
@{
var settings = await SideShiftService.GetSideShiftForInvoice(Model.InvoiceId, Model.StoreId);
if (settings?.Enabled is true)
{
csp.Add("script-src", "https://sideshift.ai");
csp.Add("script-src", "*.sideshift.ai");
<script src="~/Resources/js/sideShiftComponent.js"></script>
<script src="https://sideshift.ai/static/js/main.js" defer></script>
}
}

View File

@@ -2,19 +2,18 @@
@using BTCPayServer.Payments @using BTCPayServer.Payments
@inject BTCPayServer.Security.ContentSecurityPolicies csp @inject BTCPayServer.Security.ContentSecurityPolicies csp
@inject SideShiftService SideShiftService @inject SideShiftService SideShiftService
@model BTCPayServer.Models.InvoicingModels.PaymentModel @model BTCPayServer.Models.InvoicingModels.CheckoutModel
@{ @{
var settings = await SideShiftService.GetSideShiftForInvoice(Model.InvoiceId, Model.StoreId); var settings = await SideShiftService.GetSideShiftForInvoice(Model.InvoiceId, Model.StoreId);
var preferredTargetPaymentMethodId = ""; PaymentMethodId preferredTargetPaymentMethodId = null;
if(!PaymentMethodId.TryParse(settings?.PreferredTargetPaymentMethodId, out var preferredPMI)) if(!PaymentMethodId.TryParse(settings?.PreferredTargetPaymentMethodId, out var preferredPMI))
{ {
preferredTargetPaymentMethodId = null; preferredTargetPaymentMethodId = null;
} }
else else
{ {
preferredTargetPaymentMethodId = Model.AvailableCryptos.FirstOrDefault(crypto => preferredTargetPaymentMethodId = Model.AvailablePaymentMethods.FirstOrDefault(crypto =>
crypto.PaymentMethodId == settings.PreferredTargetPaymentMethodId || crypto.PaymentMethodId == preferredPMI )?.PaymentMethodId;
(crypto.CryptoCode == preferredPMI.CryptoCode && crypto.PaymentMethodId.EndsWith(LNURLPayPaymentType.Instance.GetId()) || crypto.PaymentMethodId.EndsWith(LightningPaymentType.Instance.GetId())))?.PaymentMethodId;
} }
} }
@if (settings?.Enabled is true) @if (settings?.Enabled is true)
@@ -25,7 +24,8 @@
<template id="side-shift-checkout-template"> <template id="side-shift-checkout-template">
<div class="payment-box"> <div class="payment-box">
<p v-html="content"></p> <p v-html="content"></p>
<button type="button" v-on:click="openDialog" class="btn btn-primary rounded-pill w-100">{{$t("Pay with SideShift")}}</button> <p v-if="!settleMethodId" class="text-danger">Lightning is not supported via Sideshift. Select another payment method first, then come back.</p>
<button v-if="settleMethodId" type="button" v-on:click="openDialog" class="btn btn-primary rounded-pill w-100">{{$t("Pay with SideShift", {crryptoCode: settleMethodId})}}</button>
</div> </div>
</template> </template>
<script> <script>
@@ -48,7 +48,7 @@
},200) },200)
if(this.preferredToCurrency && this.model.paymentMethodId !== this.preferredToCurrency){ if(this.preferredToCurrency && this.model.paymentMethodId !== this.preferredToCurrency){
if (this.model.onChainWithLnInvoiceFallback && this.model.paymentMethodId === "BTC"){ if (this.model.onChainWithLnInvoiceFallback && this.model.paymentMethodId === "BTC-CHAIN"){
return; return;
} }
this.$parent.paymentMethodId = this.preferredToCurrency; this.$parent.paymentMethodId = this.preferredToCurrency;
@@ -58,16 +58,7 @@
}, },
computed: { computed: {
lightning () {
if (!this.model.onChainWithLnInvoiceFallback || this.model.paymentMethodId !== "BTC"){
return null;
}
const index = this.model.invoiceBitcoinUrl.indexOf("lightning=");
if (index === -1){
return null;
}
return this.model.invoiceBitcoinUrl.slice(index + "lightning=".length);
},
content () { content () {
return this.$i18n.i18next.t("conversion_body", this.model).replace(/\n/ig, '<br>'); return this.$i18n.i18next.t("conversion_body", this.model).replace(/\n/ig, '<br>');
}, },
@@ -76,16 +67,16 @@
}, },
settleMethodId () { settleMethodId () {
const toCurrency = this.currency.toLowerCase(); const toCurrency = this.currency.toUpperCase();
if (toCurrency === "lbtc") { if (toCurrency === "lbtc") {
return 'liquid'; return 'liquid';
} else if (toCurrency === "usdt") { } else if (toCurrency === "usdt") {
return "usdtla"; return "usdtla";
} else if (toCurrency.endsWith('lightninglike') || toCurrency.endsWith('lnurlpay') || this.lightning) { } else if (toCurrency.endsWith('LN') || toCurrency.endsWith('LNURL')) {
return "ln"; return null;
} else { } else {
return toCurrency.replace('_btclike', '').replace('_monerolike', '').replace('_zcashlike', '').toLowerCase(); return toCurrency.replace('-CHAIN', '').replace('_CHAIN', '').toLowerCase();
} }
}, },
type () { type () {
@@ -96,16 +87,19 @@
amountDue () { amountDue () {
return this.model.isUnsetTopUp return this.model.isUnsetTopUp
? undefined ? undefined
: this.model.btcDue * (1 + (@settings.AmountMarkupPercentage / 100)); : this.model.due * (1 + (@settings.AmountMarkupPercentage / 100));
} }
}, },
methods: { methods: {
openDialog () { openDialog () {
if (!this.settleMethodId){
return;
}
window.__SIDESHIFT__ = { window.__SIDESHIFT__ = {
parentAffiliateId: "qg0OrfHJV", parentAffiliateId: "qg0OrfHJV",
defaultDepositMethodId: this.explicitId || undefined, defaultDepositMethodId: this.explicitId || undefined,
defaultSettleMethodId: this.settleMethodId, defaultSettleMethodId: this.settleMethodId,
settleAddress: this.lightning || this.model.btcAddress, settleAddress: this.model.address,
settleAmount: this.amountDue, settleAmount: this.amountDue,
type: this.type type: this.type
}; };

View File

@@ -1,6 +1,6 @@
@using BTCPayServer.Plugins.SideShift @using BTCPayServer.Plugins.SideShift
@inject SideShiftService SideShiftService @inject SideShiftService SideShiftService
@model BTCPayServer.Models.InvoicingModels.PaymentModel @model BTCPayServer.Models.InvoicingModels.CheckoutModel
@{ @{
const string id = "SideShift"; const string id = "SideShift";
var settings = await SideShiftService.GetSideShiftForInvoice(Model.InvoiceId, Model.StoreId); var settings = await SideShiftService.GetSideShiftForInvoice(Model.InvoiceId, Model.StoreId);

View File

@@ -1,12 +0,0 @@
@using BTCPayServer.Plugins.SideShift
@inject SideShiftService SideShiftService
@model BTCPayServer.Models.InvoicingModels.PaymentModel
@{
var settings = await SideShiftService.GetSideShiftForInvoice(Model.InvoiceId, Model.StoreId);
if (settings?.Enabled is true)
{
<div class="payment-tabs__tab py-0" id="sideshift-tab" v-on:click="switchTab('sideshift')" v-bind:class="{ 'active': currentTab == 'sideshift'}" v-if="!srvModel.paymentMethodId.endsWith('LNURLPAY')">
<span>{{$t("Altcoins (SideShift)")}}</span>
</div>
}
}

View File

@@ -1,212 +0,0 @@
@using BTCPayServer.Abstractions.TagHelpers
@using BTCPayServer.Plugins.SideShift
@using Microsoft.AspNetCore.Mvc.TagHelpers
@inject SideShiftService SideShiftService
@{
var coins = await SideShiftService.GetSettleCoins();
coins = coins.Where(tuple => new[] {SideShiftService.CoinType.VariableOnly, SideShiftService.CoinType.Both}.Contains(tuple.Type)).ToList();
if(coins.Any() is not true)
{
return;
}
}
<script>
const ssAvailableCoins = @Json.Serialize(coins.ToDictionary(tuple=> $"{tuple.CryptoCode}_{tuple.Network}",tuple =>
new {
coin = tuple.DisplayName,
code = tuple.CryptoCode,
memo = tuple.HasMemo,
network = tuple.Network
}));
document.addEventListener('DOMContentLoaded', (event) => {
if (new URLSearchParams(window.location.search).has("hidejunk")) {
localStorage.setItem('hidejunk', 'true');
}
if(localStorage.getItem("hidejunk")) {
[...document.querySelectorAll("#sscoin option")].forEach(option => {
const text = option.innerText.toLowerCase();
// Check if the option matches the criteria
const isMatch = (text.includes("bitcoin") || text.includes("tether") || text.includes("usd")) &&
!text.includes("bitcoincash");
// If it matches, show it; otherwise, hide it
//remove it
if (!isMatch)
option.remove();
});
}
// const sideshiftDestinationButton = document.createElement("button");
// sideshiftDestinationButton.type= "button";
// sideshiftDestinationButton.className = "btn btn-primary btn-sm";
// sideshiftDestinationButton.innerText = "Generate SideShift destination";
// document.getElementById("add-prism").insertAdjacentElement("afterend", sideshiftDestinationButton);
// const modal = new bootstrap.Modal('#sideshiftModal');
// sideshiftDestinationButton.addEventListener("click", ev => modal.show());
const selectedSideShiftCoin = document.getElementById("sscoin");
const specifiedSideShiftDestination = document.getElementById("ssdest");
const specifiedSideShiftDepositNetwork = document.getElementById("ssdepositNetwork");
const specifiedSideShiftMemo= document.getElementById("ssmemo");
const shiftButton = document.getElementById("ssshift");
let selectedCoin = null;
const destinationContainer = document.getElementById("ss-dest-info");
specifiedSideShiftDestination.addEventListener("input", ev1 => {
document.getElementById("ss-result").style.display = "none";
if (isValid()){
shiftButton.removeAttribute("disabled");
}
});
specifiedSideShiftMemo.addEventListener("input", ev1 => {
if (isValid()){
shiftButton.removeAttribute("disabled");
}else{
shiftButton.setAttribute("disabled", "disabled");
}
});
isValid = ()=>{
return selectedCoin && specifiedSideShiftDestination.value &&
(!selectedCoin.memo || specifiedSideShiftMemo.value);
};
handleSelectChanges = ()=>{
if (selectedSideShiftCoin.value){
selectedCoin = ssAvailableCoins[selectedSideShiftCoin.value];
destinationContainer.style.display = "block";
if (selectedCoin){
specifiedSideShiftMemo.parentElement.style.display = selectedCoin.memo ? "block" : "none";
specifiedSideShiftMemo.value = selectedCoin.memo ? specifiedSideShiftMemo.value : "";
}
}else{
destinationContainer.style.display = "none";
}
};
selectedSideShiftCoin.addEventListener("change", ev1 => {
handleSelectChanges();
});
shiftButton.addEventListener("click", ev1 => {
document.getElementById("ss-server-errors").innerHTML = "";
document.getElementById("ss-result-txt").value = "";
document.getElementById("ss-result-additional-info").value = "";
if (isValid()){
shiftButton.setAttribute("disabled", "disabled");
const type = "permanent";
if (type ==="permanent"){
fetch("https://sideshift.ai/api/v2/shifts/variable",{
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
settleAddress: specifiedSideShiftDestination.value,
settleMemo: specifiedSideShiftMemo.value,
affiliateId: "qg0OrfHJV",
depositCoin : "BTC",
depositNetwork : specifiedSideShiftDepositNetwork.value,
settleCoin: selectedCoin.code,
settleNetwork: selectedCoin.network,
permanent: true
})})
.then(async response => {
if (!response.ok){
try {
document.getElementById("ss-server-errors").innerHTML = (await response.json())["error"]["message"];
}catch{
document.getElementById("ss-server-errors").innerHTML = JSON.stringify((await response.json()));
}
return;
}
const shift = await response.json();
document.getElementById("ss-result").style.display = "block";
document.getElementById("ss-result-txt").value = shift.depositAddress;
const link = `https://sideshift.ai/orders/${shift.id}`;
document.getElementById("ss-result-additional-info").innerHTML = "<b>IMPORTANT:</b> You must keep this link to be able to recover your funds in case of a problem. <a href='"+link+"' target='_blank'>"+link+"</a> ";
})
.catch(error => document.getElementById("ss-server-errors").innerHTML = error)
.finally(() => shiftButton.removeAttribute("disabled"));
}else{
document.getElementById("ss-result").style.display = "block";
document.getElementById("ss-result-txt").value = "sideshift:"+JSON.stringify({
shiftCoin:selectedCoin.code,
shiftNetwork: selectedCoin.network,
shiftDestination: specifiedSideShiftDestination.value,
shiftMemo: specifiedSideShiftMemo.value,
shiftDepositNetwork: specifiedSideShiftDepositNetwork.value
});
shiftButton.removeAttribute("disabled");
}
}
});
handleSelectChanges();
});
</script>
<div class="modal" tabindex="-1" id="sideshiftModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Generate SideShift destination</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div id="ss-server-errors" class="text-danger"></div>
<p>This will generate a piece of code based on Sideshift configuration that can work as a valid destination in prism. Prism will then generate a "shift" on Sideshift and send the funds through LN to it, and Sideshift will send you the conversion. </p>
<div class="form-group">
<label class="form-label">How do you want to send BTC to SideShift?</label>
<select id="ssdepositNetwork" class="form-select">
<option value="lightning">Lightning</option>
<option value="bitcoin">On-Chain</option>
</select>
</div><div class="form-group">
<label class="form-label">Which coin should Sideshift send you</label>
<select id="sscoin" class="form-select">
@foreach (var opt in coins)
{
<option value="@($"{opt.CryptoCode}_{opt.Network}")">@opt.ToString()</option>
}
</select>
</div>
<div id="ss-dest-info" style="display: none">
<div class="form-group">
<label class="form-label">Destination</label>
<input type="text" id="ssdest" class="form-control"/>
</div>
<div class="form-group">
<label class="form-label">Memo</label>
<input type="text" id="ssmemo" class="form-control"/>
</div>
<button type="button" class="btn btn-primary" id="ssshift" disabled="disabled">Generate code</button>
<div id="ss-result" class="form-group mt-4" style="display: none;">
<label class="form-label">Generated code</label>
<div class="input-group">
<input type="text" id="ss-result-txt" class="form-control" readonly="readonly"/>
<button type="button" class="btn btn-secondary" data-clipboard-target="#ss-result-txt">
<vc:icon symbol="copy"/>
</button>
</div>
<p id="ss-result-additional-info"></p>
</div>
</div>
</div>
</div>
</div>
</div>

View File

@@ -1,4 +1,5 @@
@using BTCPayServer.Plugins.SideShift @using BTCPayServer.Payouts
@using BTCPayServer.Plugins.SideShift
@model BTCPayServer.Models.ViewPullPaymentModel @model BTCPayServer.Models.ViewPullPaymentModel
@inject SideShiftService SideShiftService @inject SideShiftService SideShiftService
@{ @{
@@ -14,7 +15,8 @@
return; return;
} }
var potentialPaymentMethods = Model.PaymentMethods;//.Where(id => id.CryptoCode.Equals(Model.Currency, StringComparison.OrdinalIgnoreCase)).ToList(); var potentialPaymentMethods = Model.PayoutMethodIds.ToList();//.Where(id => id.CryptoCode.Equals(Model.Currency, StringComparison.OrdinalIgnoreCase)).ToList();
potentialPaymentMethods.Remove(PayoutTypes.LN.GetPayoutMethodId("BTC"));
if (Model.IsPending && potentialPaymentMethods.Any()) if (Model.IsPending && potentialPaymentMethods.Any())
{ {
<script> <script>
@@ -26,7 +28,7 @@
memo = tuple.HasMemo, memo = tuple.HasMemo,
network = tuple.Network network = tuple.Network
})); }));
const ssPaymentMethods = @Json.Serialize(potentialPaymentMethods.Select(id => new { id = id.ToString(), name= id.ToPrettyString()})); const ssPaymentMethods = @Json.Serialize(potentialPaymentMethods.Select(id => new { id = id.ToString(), name= id.ToString()}));
document.addEventListener("DOMContentLoaded", ev => { document.addEventListener("DOMContentLoaded", ev => {
const ssButton = document.createElement("button"); const ssButton = document.createElement("button");
ssButton.type= "button"; ssButton.type= "button";
@@ -142,7 +144,7 @@
<select id="sspmi" class="form-select"> <select id="sspmi" class="form-select">
@foreach (var opt in potentialPaymentMethods) @foreach (var opt in potentialPaymentMethods)
{ {
<option value="@opt.ToString()">@opt.ToPrettyString()</option> <option value="@opt.ToString()">@opt.ToString()</option>
} }
</select> </select>
</div> </div>

View File

@@ -4,13 +4,14 @@
@using BTCPayServer.Plugins.SideShift @using BTCPayServer.Plugins.SideShift
@using Microsoft.AspNetCore.Mvc.TagHelpers @using Microsoft.AspNetCore.Mvc.TagHelpers
@model BTCPayServer.Plugins.SideShift.SideShiftSettings @model BTCPayServer.Plugins.SideShift.SideShiftSettings
@inject BTCPayNetworkProvider BTCPayNetworkProvider
@inject SideShiftService SideShiftService @inject SideShiftService SideShiftService
@{ @{
ViewData.SetActivePage("SideShift", "SideShift", "SideShift"); ViewData.SetActivePage("SideShift", "SideShift", "SideShift");
var store = Context.GetStoreData(); var store = Context.GetStoreData();
var allowedPaymentMethods = store.GetEnabledPaymentIds(BTCPayNetworkProvider) var allowedPaymentMethods = store.GetEnabledPaymentIds()
.Select(pmi => new SelectListItem(pmi.ToPrettyString(), pmi.ToString())) .Where(id => !id.ToString().EndsWith("LN") && !id.ToString().EndsWith("LNURL"))
.Select(pmi => new SelectListItem(pmi.ToString(), pmi.ToString()))
.Prepend(new SelectListItem("Any", "")); .Prepend(new SelectListItem("Any", ""));
var coins = await SideShiftService.GetDepositOptions(); var coins = await SideShiftService.GetDepositOptions();
var allowedCoins = coins.OrderBy(coin => coin.ToString()).Select(c => new SelectListItem(c.ToString(), $"{c.CryptoCode}_{c.Network}")); var allowedCoins = coins.OrderBy(coin => coin.ToString()).Select(c => new SelectListItem(c.ToString(), $"{c.CryptoCode}_{c.Network}"));

View File

@@ -10,7 +10,7 @@
<PropertyGroup> <PropertyGroup>
<Product>Subscriptions</Product> <Product>Subscriptions</Product>
<Description>Offer and manage subscriptions through BTCPay Server</Description> <Description>Offer and manage subscriptions through BTCPay Server</Description>
<Version>1.0.1</Version> <Version>1.0.2</Version>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup> </PropertyGroup>
<!-- Plugin development properties --> <!-- Plugin development properties -->

View File

@@ -1,14 +1,13 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using BTCPayServer;
using BTCPayServer.Abstractions.Constants; using BTCPayServer.Abstractions.Constants;
using BTCPayServer.Abstractions.Extensions; using BTCPayServer.Abstractions.Extensions;
using BTCPayServer.Client; using BTCPayServer.Client;
using BTCPayServer.Client.Models; using BTCPayServer.Client.Models;
using BTCPayServer.Data; using BTCPayServer.Data;
using BTCPayServer.Models; using BTCPayServer.Models;
using BTCPayServer.Plugins.Subscriptions; using BTCPayServer.Services;
using BTCPayServer.Services.Apps; using BTCPayServer.Services.Apps;
using BTCPayServer.Services.PaymentRequests; using BTCPayServer.Services.PaymentRequests;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
@@ -23,13 +22,16 @@ namespace BTCPayServer.Plugins.Subscriptions;
public class SubscriptionController : Controller public class SubscriptionController : Controller
{ {
private readonly AppService _appService; private readonly AppService _appService;
private readonly UriResolver _uriResolver;
private readonly PaymentRequestRepository _paymentRequestRepository; private readonly PaymentRequestRepository _paymentRequestRepository;
private readonly SubscriptionService _subscriptionService; private readonly SubscriptionService _subscriptionService;
public SubscriptionController(AppService appService, public SubscriptionController(AppService appService,
UriResolver uriResolver,
PaymentRequestRepository paymentRequestRepository, SubscriptionService subscriptionService) PaymentRequestRepository paymentRequestRepository, SubscriptionService subscriptionService)
{ {
_appService = appService; _appService = appService;
_uriResolver = uriResolver;
_paymentRequestRepository = paymentRequestRepository; _paymentRequestRepository = paymentRequestRepository;
_subscriptionService = subscriptionService; _subscriptionService = subscriptionService;
} }
@@ -44,7 +46,7 @@ public class SubscriptionController : Controller
return NotFound(); return NotFound();
var ss = app.GetSettings<SubscriptionAppSettings>(); var ss = app.GetSettings<SubscriptionAppSettings>();
ss.SubscriptionName = app.Name; ss.SubscriptionName = app.Name;
ViewData["StoreBranding"] = new StoreBrandingViewModel(app.StoreData.GetStoreBlob()); ViewData["StoreBranding"] =await StoreBrandingViewModel.CreateAsync(Request, _uriResolver, app.StoreData.GetStoreBlob());
return View(ss); return View(ss);
} }
@@ -62,8 +64,7 @@ public class SubscriptionController : Controller
{ {
return NotFound(); return NotFound();
} }
ViewData["StoreBranding"] =await StoreBrandingViewModel.CreateAsync(Request, _uriResolver, app.StoreData.GetStoreBlob());
ViewData["StoreBranding"] = new StoreBrandingViewModel(app.StoreData.GetStoreBlob());
return View(ss); return View(ss);
} }

View File

@@ -1,11 +1,9 @@
using System.IO; using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
using BTCPayServer.Abstractions.Contracts; using BTCPayServer.Abstractions.Contracts;
using BTCPayServer.Abstractions.Extensions;
using BTCPayServer.Abstractions.Models; using BTCPayServer.Abstractions.Models;
using BTCPayServer.Abstractions.Services; using BTCPayServer.Abstractions.Services;
using BTCPayServer.HostedServices.Webhooks; using BTCPayServer.HostedServices.Webhooks;
using BTCPayServer.Services;
using BTCPayServer.Services.Apps; using BTCPayServer.Services.Apps;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
@@ -18,12 +16,11 @@ namespace BTCPayServer.Plugins.Subscriptions
{ {
public override IBTCPayServerPlugin.PluginDependency[] Dependencies { get; } = public override IBTCPayServerPlugin.PluginDependency[] Dependencies { get; } =
[ [
new() {Identifier = nameof(BTCPayServer), Condition = ">=1.13.0"} new() {Identifier = nameof(BTCPayServer), Condition = ">=2.0.0"}
]; ];
public override void Execute(IServiceCollection applicationBuilder) public override void Execute(IServiceCollection applicationBuilder)
{ {
applicationBuilder.AddSingleton<ISwaggerProvider, SubscriptionsSwaggerProvider>(); applicationBuilder.AddSingleton<ISwaggerProvider, SubscriptionsSwaggerProvider>();
applicationBuilder.AddSingleton<SubscriptionService>(); applicationBuilder.AddSingleton<SubscriptionService>();
applicationBuilder.AddSingleton<IWebhookProvider>(o => o.GetRequiredService<SubscriptionService>()); applicationBuilder.AddSingleton<IWebhookProvider>(o => o.GetRequiredService<SubscriptionService>());
@@ -35,19 +32,17 @@ namespace BTCPayServer.Plugins.Subscriptions
} }
} }
public class SubscriptionsSwaggerProvider: ISwaggerProvider public class SubscriptionsSwaggerProvider : ISwaggerProvider
{ {
private readonly IFileProvider _fileProvider; private readonly IFileProvider _fileProvider;
public SubscriptionsSwaggerProvider(IWebHostEnvironment webHostEnvironment) public SubscriptionsSwaggerProvider(IWebHostEnvironment webHostEnvironment)
{ {
_fileProvider = webHostEnvironment.WebRootFileProvider; _fileProvider = webHostEnvironment.WebRootFileProvider;
} }
public async Task<JObject> Fetch() public async Task<JObject> Fetch()
{ {
var file = _fileProvider.GetFileInfo("Resources/swagger.subscriptions.json"); var file = _fileProvider.GetFileInfo("Resources/swagger.subscriptions.json");
using var reader = new StreamReader(file.CreateReadStream()); using var reader = new StreamReader(file.CreateReadStream());
return JObject.Parse(await reader.ReadToEndAsync()); return JObject.Parse(await reader.ReadToEndAsync());

View File

@@ -48,10 +48,10 @@ public class AppMigrate : IStartupTask
await using var ctx = _contextFactory.CreateContext(); await using var ctx = _contextFactory.CreateContext();
var invoices = await ctx.Invoices var invoices = await ctx.Invoices
.Include(data => data.InvoiceSearchData) .Include(data => data.InvoiceSearchData)
.Where(data => data.StoreDataId == setting.Key && data.OrderId == "tickettailor").ToListAsync(cancellationToken: cancellationToken); .Where(data => data.StoreDataId == setting.Key && data.InvoiceSearchData.Any(searchData => searchData.Value == "tickettailor")).ToListAsync(cancellationToken: cancellationToken);
foreach (var invoice in invoices) foreach (var invoice in invoices)
{ {
var entity = invoice.GetBlob(_btcPayNetworkProvider); var entity = invoice.GetBlob();
entity.Metadata.SetAdditionalData("appId", app.Id); entity.Metadata.SetAdditionalData("appId", app.Id);
entity.InternalTags.Add(AppService.GetAppInternalTag(app.Id)); entity.InternalTags.Add(AppService.GetAppInternalTag(app.Id));
InvoiceRepository.AddToTextSearch(ctx, invoice, AppService.GetAppSearchTerm(app) ); InvoiceRepository.AddToTextSearch(ctx, invoice, AppService.GetAppSearchTerm(app) );

View File

@@ -9,7 +9,7 @@
<PropertyGroup> <PropertyGroup>
<Product>TicketTailor</Product> <Product>TicketTailor</Product>
<Description>Allows you to integrate with TicketTailor.com to sell tickets for Bitcoin</Description> <Description>Allows you to integrate with TicketTailor.com to sell tickets for Bitcoin</Description>
<Version>2.0.3</Version> <Version>2.0.4</Version>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup> </PropertyGroup>
<!-- Plugin development properties --> <!-- Plugin development properties -->

View File

@@ -12,6 +12,7 @@ using BTCPayServer.Client.Models;
using BTCPayServer.Controllers; using BTCPayServer.Controllers;
using BTCPayServer.Data; using BTCPayServer.Data;
using BTCPayServer.Models; using BTCPayServer.Models;
using BTCPayServer.Services;
using BTCPayServer.Services.Apps; using BTCPayServer.Services.Apps;
using BTCPayServer.Services.Invoices; using BTCPayServer.Services.Invoices;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
@@ -28,6 +29,7 @@ namespace BTCPayServer.Plugins.TicketTailor
{ {
private readonly IHttpClientFactory _httpClientFactory; private readonly IHttpClientFactory _httpClientFactory;
private readonly TicketTailorService _ticketTailorService; private readonly TicketTailorService _ticketTailorService;
private readonly UriResolver _uriResolver;
private readonly AppService _appService; private readonly AppService _appService;
private readonly ApplicationDbContextFactory _contextFactory; private readonly ApplicationDbContextFactory _contextFactory;
private readonly InvoiceRepository _invoiceRepository; private readonly InvoiceRepository _invoiceRepository;
@@ -35,6 +37,7 @@ namespace BTCPayServer.Plugins.TicketTailor
public TicketTailorController(IHttpClientFactory httpClientFactory, public TicketTailorController(IHttpClientFactory httpClientFactory,
TicketTailorService ticketTailorService, TicketTailorService ticketTailorService,
UriResolver uriResolver,
AppService appService, AppService appService,
ApplicationDbContextFactory contextFactory, ApplicationDbContextFactory contextFactory,
InvoiceRepository invoiceRepository, InvoiceRepository invoiceRepository,
@@ -42,6 +45,7 @@ namespace BTCPayServer.Plugins.TicketTailor
{ {
_httpClientFactory = httpClientFactory; _httpClientFactory = httpClientFactory;
_ticketTailorService = ticketTailorService; _ticketTailorService = ticketTailorService;
_uriResolver = uriResolver;
_appService = appService; _appService = appService;
_contextFactory = contextFactory; _contextFactory = contextFactory;
_invoiceRepository = invoiceRepository; _invoiceRepository = invoiceRepository;
@@ -87,7 +91,7 @@ namespace BTCPayServer.Plugins.TicketTailor
return View(new TicketTailorViewModel() return View(new TicketTailorViewModel()
{ {
Event = evt, Settings = config, Event = evt, Settings = config,
StoreBranding = new StoreBrandingViewModel(app.StoreData.GetStoreBlob()) StoreBranding = await StoreBrandingViewModel.CreateAsync(Request, _uriResolver, app.StoreData.GetStoreBlob())
}); });
} }
} }
@@ -270,7 +274,6 @@ namespace BTCPayServer.Plugins.TicketTailor
AdditionalSearchTerms = new[] {"tickettailor", hold.Value.Item1.Id, evt.Id, AppService.GetAppSearchTerm(app)}, AdditionalSearchTerms = new[] {"tickettailor", hold.Value.Item1.Id, evt.Id, AppService.GetAppSearchTerm(app)},
Checkout = Checkout =
{ {
RequiresRefundEmail = true,
RedirectAutomatically = price > 0, RedirectAutomatically = price > 0,
RedirectURL = redirectUrl, RedirectURL = redirectUrl,
}, },
@@ -292,13 +295,13 @@ namespace BTCPayServer.Plugins.TicketTailor
}, app.StoreData, HttpContext.Request.GetAbsoluteRoot(),new List<string> { AppService.GetAppInternalTag(appId) }, CancellationToken.None); }, app.StoreData, HttpContext.Request.GetAbsoluteRoot(),new List<string> { AppService.GetAppInternalTag(appId) }, CancellationToken.None);
while (inv.Price == 0 && inv.Status == InvoiceStatusLegacy.New) while (inv.Price == 0 && inv.Status == InvoiceStatus.New)
{ {
if (inv.Status == InvoiceStatusLegacy.New) if (inv.Status == InvoiceStatus.New)
inv = await _invoiceRepository.GetInvoice(inv.Id); inv = await _invoiceRepository.GetInvoice(inv.Id);
} }
return inv.Status.ToModernStatus() == InvoiceStatus.Settled return inv.Status == InvoiceStatus.Settled
? RedirectToAction("Receipt", new {invoiceId = inv.Id}) ? RedirectToAction("Receipt", new {invoiceId = inv.Id})
: RedirectToAction("Checkout", "UIInvoice", new {invoiceId = inv.Id}); : RedirectToAction("Checkout", "UIInvoice", new {invoiceId = inv.Id});
} }
@@ -342,13 +345,13 @@ namespace BTCPayServer.Plugins.TicketTailor
var appId = AppService.GetAppInternalTags(inv).First(); var appId = AppService.GetAppInternalTags(inv).First();
var result = new TicketReceiptPage() {InvoiceId = invoiceId}; var result = new TicketReceiptPage() {InvoiceId = invoiceId};
result.Status = inv.Status.ToModernStatus(); result.Status = inv.Status;
if (result.Status == InvoiceStatus.Settled && if (result.Status == InvoiceStatus.Settled &&
inv.Metadata.AdditionalData.TryGetValue("ticketIds", out var ticketIds)) inv.Metadata.AdditionalData.TryGetValue("ticketIds", out var ticketIds))
{ {
await SetTicketTailorTicketResult(appId, result, ticketIds.Values<string>()); await SetTicketTailorTicketResult(appId, result, ticketIds.Values<string>());
} }
else if (inv.Status.ToModernStatus() == InvoiceStatus.Settled) else if (inv.Status == InvoiceStatus.Settled)
{ {
await _ticketTailorService.CheckAndIssueTicket(inv.Id); await _ticketTailorService.CheckAndIssueTicket(inv.Id);
} }

View File

@@ -12,7 +12,7 @@ namespace BTCPayServer.Plugins.TicketTailor
{ {
public override IBTCPayServerPlugin.PluginDependency[] Dependencies { get; } = public override IBTCPayServerPlugin.PluginDependency[] Dependencies { get; } =
{ {
new() {Identifier = nameof(BTCPayServer), Condition = ">=1.12.0"} new() {Identifier = nameof(BTCPayServer), Condition = ">=2.0.0"}
}; };
public override void Execute(IServiceCollection applicationBuilder) public override void Execute(IServiceCollection applicationBuilder)

View File

@@ -101,8 +101,7 @@ public class TicketTailorService : EventHostedServiceBase, IWebhookProvider
!new[] !new[]
{ {
InvoiceStatus.Settled, InvoiceStatus.Expired, InvoiceStatus.Invalid InvoiceStatus.Settled, InvoiceStatus.Expired, InvoiceStatus.Invalid
}.Contains(invoiceEvent.Invoice.GetInvoiceState().Status }.Contains(invoiceEvent.Invoice.GetInvoiceState().Status):
.ToModernStatus()):
return; return;
case InvoiceEvent invoiceEvent: case InvoiceEvent invoiceEvent:
@@ -157,7 +156,7 @@ public class TicketTailorService : EventHostedServiceBase, IWebhookProvider
return; return;
} }
if (new[] {InvoiceStatus.Invalid, InvoiceStatus.Expired}.Contains(invoice.Status.ToModernStatus())) if (new[] {InvoiceStatus.Invalid, InvoiceStatus.Expired}.Contains(invoice.Status))
{ {
if (invoice.Metadata.AdditionalData.TryGetValue("holdId", out var jHoldIdx) && if (invoice.Metadata.AdditionalData.TryGetValue("holdId", out var jHoldIdx) &&
@@ -178,7 +177,7 @@ public class TicketTailorService : EventHostedServiceBase, IWebhookProvider
return; return;
} }
if (invoice.Status.ToModernStatus() != InvoiceStatus.Settled) if (invoice.Status != InvoiceStatus.Settled)
{ {
return; return;
} }

View File

@@ -13,7 +13,7 @@
<PropertyGroup> <PropertyGroup>
<Product>Coinjoin</Product> <Product>Coinjoin</Product>
<Description>Allows you to integrate your btcpayserver store with coinjoins.</Description> <Description>Allows you to integrate your btcpayserver store with coinjoins.</Description>
<Version>1.0.100</Version> <Version>1.0.101</Version>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup> </PropertyGroup>

View File

@@ -11,7 +11,9 @@ using BTCPayServer.Data;
using BTCPayServer.HostedServices; using BTCPayServer.HostedServices;
using BTCPayServer.Payments; using BTCPayServer.Payments;
using BTCPayServer.Payments.PayJoin; using BTCPayServer.Payments.PayJoin;
using BTCPayServer.Payouts;
using BTCPayServer.Services; using BTCPayServer.Services;
using BTCPayServer.Services.Invoices;
using BTCPayServer.Services.Stores; using BTCPayServer.Services.Stores;
using BTCPayServer.Services.Wallets; using BTCPayServer.Services.Wallets;
using LinqKit; using LinqKit;
@@ -37,12 +39,14 @@ using WalletWasabi.WabiSabi.Backend.Rounds;
using WalletWasabi.WabiSabi.Client; using WalletWasabi.WabiSabi.Client;
using WalletWasabi.Wallets; using WalletWasabi.Wallets;
using LogLevel = WalletWasabi.Logging.LogLevel; using LogLevel = WalletWasabi.Logging.LogLevel;
using SecureRandom = WalletWasabi.Crypto.Randomness.SecureRandom;
namespace BTCPayServer.Plugins.Wabisabi; namespace BTCPayServer.Plugins.Wabisabi;
public class BTCPayWallet : IWallet, IDestinationProvider public class BTCPayWallet : IWallet, IDestinationProvider
{ {
private readonly PaymentMethodHandlerDictionary _paymentMethodHandlerDictionary;
private readonly WalletRepository _walletRepository; private readonly WalletRepository _walletRepository;
private readonly BTCPayNetworkProvider _btcPayNetworkProvider; private readonly BTCPayNetworkProvider _btcPayNetworkProvider;
private readonly BitcoinLikePayoutHandler _bitcoinLikePayoutHandler; private readonly BitcoinLikePayoutHandler _bitcoinLikePayoutHandler;
@@ -56,6 +60,7 @@ public class BTCPayWallet : IWallet, IDestinationProvider
public static readonly BlockchainAnalyzer BlockchainAnalyzer = new(); public static readonly BlockchainAnalyzer BlockchainAnalyzer = new();
public BTCPayWallet( public BTCPayWallet(
PaymentMethodHandlerDictionary paymentMethodHandlerDictionary,
WalletRepository walletRepository, WalletRepository walletRepository,
BTCPayNetworkProvider btcPayNetworkProvider, BTCPayNetworkProvider btcPayNetworkProvider,
BitcoinLikePayoutHandler bitcoinLikePayoutHandler, BitcoinLikePayoutHandler bitcoinLikePayoutHandler,
@@ -75,6 +80,7 @@ public class BTCPayWallet : IWallet, IDestinationProvider
WalletId = new WalletWasabi.Wallets.WalletId(new Guid(SHA256.HashData(Encoding.UTF8.GetBytes(storeId)).Take(16) WalletId = new WalletWasabi.Wallets.WalletId(new Guid(SHA256.HashData(Encoding.UTF8.GetBytes(storeId)).Take(16)
.ToArray())); .ToArray()));
KeyChain = keyChain; KeyChain = keyChain;
_paymentMethodHandlerDictionary = paymentMethodHandlerDictionary;
_walletRepository = walletRepository; _walletRepository = walletRepository;
_btcPayNetworkProvider = btcPayNetworkProvider; _btcPayNetworkProvider = btcPayNetworkProvider;
_bitcoinLikePayoutHandler = bitcoinLikePayoutHandler; _bitcoinLikePayoutHandler = bitcoinLikePayoutHandler;
@@ -495,7 +501,7 @@ public class BTCPayWallet : IWallet, IDestinationProvider
if (storeIdForutxo != StoreId) if (storeIdForutxo != StoreId)
{ {
var s = await _storeRepository.FindStore(storeIdForutxo); var s = await _storeRepository.FindStore(storeIdForutxo);
var scheme = s.GetDerivationSchemeSettings(_btcPayNetworkProvider, "BTC"); var scheme = s.GetDerivationSchemeSettings(_paymentMethodHandlerDictionary, "BTC");
utxoDerivationScheme = scheme.AccountDerivation; utxoDerivationScheme = scheme.AccountDerivation;
} }
@@ -689,7 +695,7 @@ public async Task<IEnumerable<IDestination>> GetNextDestinationsAsync(int count,
try try
{ {
var mixStore = await _storeRepository.FindStore(WabisabiStoreSettings.MixToOtherWallet); var mixStore = await _storeRepository.FindStore(WabisabiStoreSettings.MixToOtherWallet);
var pm = mixStore.GetDerivationSchemeSettings(_btcPayNetworkProvider, "BTC"); var pm = mixStore.GetDerivationSchemeSettings(_paymentMethodHandlerDictionary, "BTC");
if (pm?.AccountDerivation?.ScriptPubKeyType() == DerivationScheme.ScriptPubKeyType()) if (pm?.AccountDerivation?.ScriptPubKeyType() == DerivationScheme.ScriptPubKeyType())
@@ -718,20 +724,19 @@ public async Task<IEnumerable<IDestination>> GetNextDestinationsAsync(int count,
{ {
States = new [] {PayoutState.AwaitingPayment}, States = new [] {PayoutState.AwaitingPayment},
Stores = new []{StoreId}, Stores = new []{StoreId},
PaymentMethods = new []{"BTC"} PayoutMethods = new []{ PayoutTypes.CHAIN.GetPayoutMethodId("BTC").ToString()}
})).Select(async data => })).Select(async data =>
{ {
var claim = await _bitcoinLikePayoutHandler.ParseClaimDestination(new PaymentMethodId("BTC", BitcoinPaymentType.Instance), var payoutBlob = data.GetBlob(_btcPayNetworkJsonSerializerSettings);
data.Destination, CancellationToken.None); var claim = await _bitcoinLikePayoutHandler.ParseClaimDestination(payoutBlob.Destination, CancellationToken.None);
if (!string.IsNullOrEmpty(claim.error) || claim.destination is not IBitcoinLikeClaimDestination bitcoinLikeClaimDestination ) if (!string.IsNullOrEmpty(claim.error) || claim.destination is not IBitcoinLikeClaimDestination bitcoinLikeClaimDestination || data.Amount is null)
{ {
return null; return null;
} }
var payoutBlob = data.GetBlob(_btcPayNetworkJsonSerializerSettings); var value = new Money(data.Amount.Value, MoneyUnit.BTC);
var value = new Money(payoutBlob.CryptoAmount.Value, MoneyUnit.BTC);
return new PendingPayment() return new PendingPayment()
{ {
Identifier = data.Id, Identifier = data.Id,

View File

@@ -5,6 +5,7 @@ using System.Net.Http;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using BTCPayServer.Services.Invoices;
using BTCPayServer.Services.Stores; using BTCPayServer.Services.Stores;
using BTCPayServer.Services.Wallets; using BTCPayServer.Services.Wallets;
using NBitcoin; using NBitcoin;
@@ -17,16 +18,19 @@ public class WabisabiScriptResolver: WabiSabiConfig.CoordinatorScriptResolver
{ {
private readonly IHttpClientFactory _httpClientFactory; private readonly IHttpClientFactory _httpClientFactory;
private readonly StoreRepository _storeRepository; private readonly StoreRepository _storeRepository;
private readonly PaymentMethodHandlerDictionary _paymentMethodHandlerDictionary;
private readonly BTCPayNetworkProvider _networkProvider; private readonly BTCPayNetworkProvider _networkProvider;
private readonly BTCPayWalletProvider _walletProvider; private readonly BTCPayWalletProvider _walletProvider;
public WabisabiScriptResolver(IHttpClientFactory httpClientFactory, public WabisabiScriptResolver(IHttpClientFactory httpClientFactory,
StoreRepository storeRepository, StoreRepository storeRepository,
PaymentMethodHandlerDictionary paymentMethodHandlerDictionary,
BTCPayNetworkProvider networkProvider, BTCPayNetworkProvider networkProvider,
BTCPayWalletProvider walletProvider) BTCPayWalletProvider walletProvider)
{ {
_httpClientFactory = httpClientFactory; _httpClientFactory = httpClientFactory;
_storeRepository = storeRepository; _storeRepository = storeRepository;
_paymentMethodHandlerDictionary = paymentMethodHandlerDictionary;
_networkProvider = networkProvider; _networkProvider = networkProvider;
_walletProvider = walletProvider; _walletProvider = walletProvider;
} }
@@ -61,7 +65,7 @@ public class WabisabiScriptResolver: WabiSabiConfig.CoordinatorScriptResolver
var store = await _storeRepository.FindStore(value); var store = await _storeRepository.FindStore(value);
var cryptoCode = _networkProvider.GetAll().OfType<BTCPayNetwork>() var cryptoCode = _networkProvider.GetAll().OfType<BTCPayNetwork>()
.First(payNetwork => payNetwork.NBitcoinNetwork == network); .First(payNetwork => payNetwork.NBitcoinNetwork == network);
var dss = store.GetDerivationSchemeSettings(_networkProvider, cryptoCode.CryptoCode); var dss = store.GetDerivationSchemeSettings(_paymentMethodHandlerDictionary, cryptoCode.CryptoCode);
var w = _walletProvider.GetWallet(cryptoCode.CryptoCode); var w = _walletProvider.GetWallet(cryptoCode.CryptoCode);
var kpi = await w.ReserveAddressAsync(store.Id, dss.AccountDerivation, "wabisabi coordinator"); var kpi = await w.ReserveAddressAsync(store.Id, dss.AccountDerivation, "wabisabi coordinator");
return kpi.ScriptPubKey; return kpi.ScriptPubKey;

Some files were not shown because too many files have changed in this diff Show More