Compare commits

..

447 Commits

Author SHA1 Message Date
8a5a160645 bump 2024-11-04 13:12:35 +09:00
5cbadc09f9 Changelog 2.0.1 2024-11-04 13:08:57 +09:00
7aa87d397e Fix: Wrong manifest downloaded when installing plugin on old btcpay (Fix ) () 2024-11-04 13:05:10 +09:00
693eceb80f Reolve pull payment timezone () 2024-11-01 08:28:43 +09:00
7d8fc14159 fix: save proof blob if payout is in progress ()
the payout cant be tracked later otherwise and will be marked as
cancelled
2024-11-01 08:24:21 +09:00
4687bb95cb Fix: Incorrect percentage accounting of raised money in crowdfunding () 2024-11-01 08:23:10 +09:00
e3ec07da76 Fix: Crowdfund page was crashing from 2.0.0 () () 2024-10-31 23:42:18 +09:00
910801d305 Replace font-awesome icon on Policies page 2024-10-31 12:23:30 +01:00
5ad0b128aa Dummy commit 2024-10-30 23:39:11 +09:00
5cbeea4fb3 Changelog 2.0 ()
* Changelog 2.0

* Update Changelog.md

---------

Co-authored-by: Andrew Camilleri <evilkukka@gmail.com>
2024-10-30 15:35:00 +01:00
a6e18736d6 Keypad updates ()
* Add keypad icons

Closes .

* Keypad JS fixes
2024-10-29 23:44:37 +09:00
373b90e3b5 Liquid fixes ()
make sure link provider is per payment method of liquid assets. Also remove ETB as it has been unused. Also hide the send button as it is not supported thrrough BTCPay
2024-10-29 23:43:37 +09:00
92f9b226fe Prevent additional concurrency issues with LightnignPendingPayoutListener 2024-10-28 22:12:29 +09:00
0ac6553840 Add download icon 2024-10-28 08:25:30 +01:00
41a2241ae1 feat: log download button ()
* feat: add download button to logs view

* fix: add using block for `fileStream` if it isnt downloaded
2024-10-27 21:43:47 +09:00
9bb1a5b80a Prevent concurrency race on lightning payout update 2024-10-27 19:55:30 +09:00
0e59107eee Fix tests with LightningPendingPayoutListener overriding automated payouts state changes 2024-10-27 19:34:20 +09:00
c9fe68b812 fix: pass current offset to log route ()
the current offset is lost otherwise and will cause a 404 if it was
greater than 0
2024-10-27 19:12:39 +09:00
e7b9688602 refactor: make BitcoinCheckoutModelExtension support other payment handlers ()
* refactor: make `BitcoinCheckoutModelExtension` support other payment handlers

The bitcoin checkout extension doesn't have to be tied to the native
bitcoin handler since it only really needs the payment details to be in
a specific format, which can be provided by other handlers aswell,
allowing for better code reuse.

* refactor: initialize payment methods in constructor
2024-10-25 22:50:46 +09:00
a962e60de9 More Translations ()
* Store selector

* Footer

* Notifications

* Checkout Appearance

* Users list

* Forms

* Emails

* Pay Button

* Edit Dictionary

* Remove newlines, fix typos

* Forms

* Pull payments and payouts

* Various pages

* Use local docs link

* Fix

* Even more translations

* Fixes 

* Account pages

* Notifications

* Placeholders

* Various pages and components

* Add more
2024-10-25 22:48:53 +09:00
e5611f9165 Fix tests () 2024-10-25 22:23:27 +09:00
540ad13265 Paging improvements ()
* Domain Mapping: Passthrough query params when redirecting

* Clean up Pager

* Use current URL when paging

* Refactor
2024-10-25 22:23:03 +09:00
2849426092 Checkout: Allow breaking long item description texts 2024-10-25 13:11:26 +02:00
c4a2b4e975 Merge pull request from btcpayserver/bugfix/vaulticons
Properly cleaning up old feedback in vault feedback items
2024-10-24 15:33:44 -05:00
d508f5dc09 Properly cleaning up old feedback in vault feedback items 2024-10-22 21:50:22 -05:00
81ce8b0469 Fix flaky test 2024-10-23 00:17:02 +09:00
5a3a661e91 Revert "Revert "Fix flaky test""
This reverts commit bb5c6bd68d15bab091e3835c2d51687da8a55def.
2024-10-22 23:44:16 +09:00
bb5c6bd68d Revert "Fix flaky test"
This reverts commit 9dfabeab52644ba863230be39eca7496f4377365.
2024-10-22 23:41:41 +09:00
9dfabeab52 Fix flaky test 2024-10-22 23:36:12 +09:00
ad07330bf1 Update CLN support to 24.08.2 () 2024-10-21 00:25:50 +09:00
74011e50e3 Do not translate checkout with the backend language 2024-10-20 11:49:36 +09:00
3dfdbf544a Automated processor get disabled after some repeated failures () 2024-10-20 00:08:28 +09:00
4bf0b79c2a Simple rename 2024-10-19 22:07:20 +09:00
cc0ea0b3f8 Refactor payouts processing () 2024-10-19 21:33:34 +09:00
62d765125d Toggle color fix 2024-10-18 15:14:30 +02:00
b5b45d9a27 Rename Transaction->Translation 2024-10-18 16:06:51 +09:00
8e098710c1 Require non interactivity for boltcard payments () 2024-10-18 14:09:41 +09:00
6dfb369b55 UI: Inactive toggle hover color fix () 2024-10-18 14:08:34 +09:00
817522ff97 refactor(checkout): displayed payment methods vue component ()
The displayed payment methods can change with updates aswell, so it
should be rendered as a vue component instead
2024-10-18 14:05:00 +09:00
8b5b90d247 refactor: make BeforeFetchingRates function public ()
This function is useful when a payment method wants to update its
payment prompt after receiving a partial payment, like ln does
2024-10-18 14:03:37 +09:00
b670097592 feat: provide store info to modify-lnurlp-request filter ()
adds store data to the filter using a new `StoreLNURLPayRequest` class
which simply adds a `Store` member.

closes: https://github.com/btcpayserver/btcpayserver/issues/6301
2024-10-18 14:03:07 +09:00
7b6a115adc [Greenfield] Select default payoutMethodId if none are selected in the Refund route () 2024-10-17 22:54:59 +09:00
77fba4aee3 Add more translations ()
* Newlines

* Dashboard

* Add more translations

* Moar

* Remove &nbsp; from translated texts

* Dictionary controller translations

* Batch 1 of controller updates

* Batch 2 of controller updates

* Component translations

* Batch 3 of controller updates

* Fixes
2024-10-17 22:51:40 +09:00
7e1712c8cd Merge pull request from NicolasDorier/fixmonero
Fix monero payments
2024-10-17 20:40:05 +09:00
b7affb1d34 Fix monero payments 2024-10-17 18:55:00 +09:00
d7fd90c4c3 Bump client lib 2024-10-17 16:41:36 +09:00
1d94782463 Merge pull request from NicolasDorier/fixelements
Fix elements payments
2024-10-16 22:34:32 +09:00
c7a05c3f09 Fix elements payments 2024-10-16 22:34:17 +09:00
2dc58a82b7 Item editor: Do not use only known props for diff ()
Brooke the file seller plugin which adds a property and this did not detect the item change
2024-10-16 14:25:06 +02:00
755dbbab00 fix server nav ui extensin () 2024-10-16 14:24:56 +02:00
b470fe22f1 Remove potential NRE 2024-10-16 17:04:27 +09:00
5b2560ddf7 Merge pull request from NicolasDorier/cleanupetw
Cleanup useless code
2024-10-16 16:38:15 +09:00
be429c527c Cleanup useless code 2024-10-16 16:25:16 +09:00
65fd537200 Merge pull request from btcpayserver/feat/lnd-0.18.3
Bumping LND to 0.18.3-beta
2024-10-15 18:49:41 -05:00
6e43c7f06f Bumping LND to 0.18.3-beta 2024-10-15 18:21:26 -05:00
5867b5c000 Merge pull request from NicolasDorier/fioqnt
Pretty names of payment methods isn't provided by CheckoutExtensions
2024-10-15 23:28:42 +09:00
05887cf8b0 Fix potential stack overflow 2024-10-15 23:11:28 +09:00
c43721d489 Pretty names of payment methods isn't provided by CheckoutExtensions 2024-10-14 21:53:14 +09:00
0bf75d52d7 Merge pull request from NicolasDorier/addtranslations
Add translations to the Dashboard and more
2024-10-14 19:45:06 +09:00
c35af2dc69 Add translations to the Dashboard 2024-10-14 19:19:56 +09:00
73a9835a27 Fix build warning 2024-10-13 00:17:49 +09:00
87ab15f754 Fix crash on Monero/ZCash on invoices list 2024-10-13 00:10:49 +09:00
6bc608c081 Merge pull request from btcpayserver/feat/plugin-search
Support for searching plugins by name
2024-10-11 08:05:27 -05:00
cbea1d8691 Merge pull request from NicolasDorier/markedfordeletion
Improve UX for uninstalling disabled plugins
2024-10-11 21:41:01 +09:00
58f21a69aa Improve UX for uninstalling disabled plugins 2024-10-11 19:35:37 +09:00
426c5b9a24 Merge pull request from NicolasDorier/plugincrashdetect
Disable plugins crashing at startup
2024-10-11 10:56:46 +09:00
511e90efd1 Disable plugins crashing at startup 2024-10-11 10:50:49 +09:00
bc7b856654 Start sending BTCPay version string to help with filtering on plugin-builder side 2024-10-10 05:46:07 -05:00
d50d2f9ca0 Merge pull request from NicolasDorier/bettershowwarnings
Show warnings if NFC payment isn't complete
2024-10-10 19:23:42 +09:00
1b53defab3 Show warnings if NFC payment isn't complete 2024-10-10 19:16:09 +09:00
3e612921f3 Remove flaky test 2024-10-10 17:50:18 +09:00
ec51d43490 Improve error message if LNWithdraw fails 2024-10-10 17:24:19 +09:00
80dc5028f7 Fix migration crashes for instance having monero, zcash 2024-10-10 11:13:23 +09:00
2329c4a75f Support for searching plugins by name 2024-10-09 06:47:11 -05:00
ae76cc1ca2 Small UI improvements in the payout processors 2024-10-09 17:44:19 +09:00
e4f79f046a Remove unused field from automated payout settings 2024-10-09 13:13:10 +09:00
622d837ea1 Merge pull request from NicolasDorier/efwoiiqnf
Prevent double BOLT11 payment with LNUrlWithdraw
2024-10-09 13:10:17 +09:00
c0aa9a8bd4 Prevent double BOLT11 payment with LNUrlWithdraw 2024-10-09 13:10:04 +09:00
9b1052f023 Remove useless code 2024-10-08 21:25:37 +09:00
c77c2f8bd6 Merge pull request from btcpayserver/addfontawesome
Add fontawesome back
2024-10-08 21:19:43 +09:00
402eaa8f12 Add fontawesome back 2024-10-08 21:17:02 +09:00
212e8c3654 Fix potential crash in migration 2024-10-08 16:48:56 +09:00
7c77b16517 Fix potential crash on migration 2024-10-08 16:30:21 +09:00
e5bb0bcba3 Fix forgotten save 2024-10-08 16:25:11 +09:00
ca4a7d8771 Migrate excludedPaymentMethods from stores 2024-10-08 16:21:44 +09:00
663f97265a Merge pull request from NicolasDorier/activationbug
Fix: An unactivated payment method failing to activate would crash the checkout
2024-10-08 15:50:28 +09:00
b91f3048ef Fix: An unactivated payment method failing to activate would crash the checkout 2024-10-08 15:07:32 +09:00
4fe0bf1236 Merge pull request from NicolasDorier/refactorcheckout
Allow payment methods to modify all the payment model
2024-10-07 21:57:00 +09:00
dd35af3c55 Use AddUIExtension 2024-10-07 21:43:06 +09:00
68f24e47cd Rename more legacy fields 2024-10-07 21:22:03 +09:00
968223a953 Rename PaymentModel to CheckoutModel 2024-10-07 19:58:08 +09:00
2f287874e3 Rename legacy fields 2024-10-07 19:51:50 +09:00
c35e7406cd Cleanup AvailableCrypto from the model 2024-10-07 19:15:40 +09:00
34b2cca492 Simplify extension of payments extensions 2024-10-07 18:37:38 +09:00
e1bfc04451 Move checkout registration to the UI Extension 2024-10-07 17:38:02 +09:00
ef0ba7b0c4 Remove useless properties 2024-10-07 16:18:09 +09:00
0a2d8880ba Remove CheckoutBodyVueComponentName 2024-10-07 15:20:26 +09:00
8dcd7e6966 Remove no javascript for checkout 2024-10-07 15:18:41 +09:00
b744fd6167 Allow payment methods to modify all the payment model 2024-10-07 14:53:21 +09:00
5bcc5c919a Improve logging of rates in invoices () 2024-10-07 09:38:09 +09:00
01e12329e9 Remove additional cryptoCode from events () 2024-10-07 09:37:56 +09:00
471bf57835 Merge branch 'master' of github.com:btcpayserver/btcpayserver 2024-10-04 23:34:52 +09:00
b246beab3e Add the concept of RateDivisibility () 2024-10-04 23:34:31 +09:00
abc8161a08 Add the concept of RateDivisibility 2024-10-04 23:14:38 +09:00
64ba8248d2 Can inject currency data in CurrencyNameTable () 2024-10-04 22:24:44 +09:00
206d222455 Fix missing interpolation marker 2024-10-04 15:18:06 +02:00
2e114d7c29 Remove references to cryptoCode in SyncStatus () 2024-10-04 16:58:31 +09:00
5190c25be0 OnlyIfSupportAttribute should use PaymentMethodId () 2024-10-04 16:58:24 +09:00
5704919b3a BlockExplorer links should be using payment method ids () 2024-10-04 16:58:13 +09:00
c3e51f51b6 Fix warnings 2024-10-03 21:51:07 +09:00
8c35edb6e8 UI: Additional improvements to the User Invitation flow ()
* UI: Additional improvements to the User Invitation flow

Closes .

* Clear invitation token only after the user can sign in

Fixes "404 Error on Follow-Up Visits" of .

* Minor spacing fix

* Update accordion button
2024-10-03 21:35:01 +09:00
2f2b4094f6 [UI] Do not show unabled payment methods in invoice creation () 2024-10-03 21:34:09 +09:00
413a9b4269 Add translation for store rate and wallet setup () 2024-10-03 19:21:19 +09:00
a698aa8a5b Do not crash if payment method disabled when store supports it 2024-10-03 19:21:01 +09:00
0f79526566 Do not make the test framework depends on CurrentDirectory 2024-10-03 16:04:16 +09:00
1ffbab7338 Small improvements to make development of plugins easier () 2024-10-03 15:16:21 +09:00
3a71c45a89 Add updated image upload support on Crowdfund plugin ()
* Add updated image upload support on Crowdfund plugin

* Refactor crowdfund image upload fix

* update crowdfund url for greenfield api

* Resolve integration test assertion

* Remove superfluous and unused command argument

* Fix missing validation error

* Minor API controller update

* Property and usage fixes

* Fix test after merge

---------

Co-authored-by: Dennis Reimann <mail@dennisreimann.de>
2024-10-03 10:39:41 +09:00
8f062f918b Add comments 2024-10-03 10:35:47 +09:00
2f05d00219 V2 compatibility: Re-add deprecated navigation methods ()
Gives the new methods a new name and re-adds the old ones in order to not break plugins. Simple enough backwartds compatible change, makred the old methods as obsolete to make plugin developers aware that new methods are available.
2024-10-02 08:25:43 +09:00
b48ca92675 Fix app stats sorting () 2024-10-02 08:23:25 +09:00
4a31cf0a09 Migrate payment requests () 2024-10-01 16:07:51 +09:00
82620ee327 Move wallet payment settings back to store settings ()
Intermediate solution, until we implement these settings on the payment method level. Closes .
2024-09-30 19:13:51 +09:00
6d284b4124 Give time for pollers to detect payments after server restart 2024-09-27 15:48:16 +09:00
83fa8cbf0f prevent app creation without wallet creation ()
* prevent app creation without wallet creation

* resolve test failures

* resolve selenium test
2024-09-27 15:28:55 +09:00
9ba4b030ed Fix: Do not expose xpub without modify store permission () 2024-09-27 15:27:04 +09:00
272cc3d3c9 POS: Option for user sign in via the QR code ()
* Login Code: Turn into Blazor component and extend with data for the app

* POS: Add login code for POS frontend

* Improve components, fix test
2024-09-26 19:10:14 +09:00
b5590a38fe Add better error message if v1 routes are used. 2024-09-26 19:09:27 +09:00
443a350bad App Service: Validate IDs when parsing items template ()
Validates missing and duplicate IDs on the edit actions and when creating/updating apps via the API.
Fails gracefully by excluding existing items without ID or with duplicate ID for the rest of the cases.

Fixes .
2024-09-26 15:52:16 +09:00
7013e618de Remove dead fields from swagger 2024-09-26 12:23:41 +09:00
363b60385b Renaming various properties in the Payouts API ()
* Rename Payouts Currency/OriginalCurrency

* Rename Payout Processor PayoutMethodIds

* Rename paymentMethods to payoutMethodIds

* Rename payoutMethodIds to payoutMethods
2024-09-26 11:25:45 +09:00
90635ffc4e Remove BTCPAY_EXPERIMENTALV2_CONFIRM 2024-09-25 23:11:53 +09:00
056f850268 Optimize load time of StoreRoles related pages/routes () 2024-09-25 23:10:13 +09:00
336f2d88e9 Fix flaky CanManageWallet
Clicking on Sign Transaction in the Wallet Send page, will, when a hot
wallet is setup, use PostRedirect page to redirect to the
broadcast screen. The problem was that sometimes, s.Driver.PageSource
would return this PostRedirect page rather than the broadcast page.
Waiting for an element of the broadcast page fixes this issue.
2024-09-25 21:53:15 +09:00
e16b4062b5 Fix payout processor migration 2024-09-25 18:50:49 +09:00
747dacf3b1 Consolidate migrations from alpha () 2024-09-25 18:23:10 +09:00
f00a71922f Optimize queries from payout processor at startup 2024-09-24 23:39:05 +09:00
c97c9d4ece Add SQL test for GetMonitoredInvoices 2024-09-24 22:07:02 +09:00
9d3f8672d9 Fix GetMonitoredInvoices 2024-09-24 17:21:36 +09:00
fe48cd4236 fix InvoiceRepository.GetMonitoredInvoices () 2024-09-24 15:44:51 +09:00
587d3aa612 Fix query 2024-09-24 09:52:28 +09:00
8a951940fd Remove dead property 2024-09-24 09:47:46 +09:00
b726ef8a2e Migrate PayoutProcessors's PayoutMethodId in entity migration 2024-09-24 09:43:02 +09:00
25e360e175 Allow listeners to retrieve invoices with nonActivated prompts 2024-09-24 08:43:30 +09:00
1d9ec253fb Fix migration of Invoice's payment () 2024-09-23 23:59:18 +09:00
3cf1aa00fa Payments should use composite key ()
* Payments should use composite key

* Invert PK for InvoiceAddress
2024-09-23 17:06:56 +09:00
36a5d0ee3f Fix: Monero and ZCash not tracking addresses 2024-09-22 11:13:09 +09:00
f5e5174045 Refactor: Add GetMonitoredInvoices to fetch pending invoices or those with pending payments () 2024-09-20 18:54:36 +09:00
ba2301ebfe Refactor the InvoiceAddresses table () 2024-09-19 22:15:02 +09:00
df651a2157 Fix GetInvoicesWithPendingPayments 2024-09-18 18:11:30 +09:00
2d2c1d5f2d fix: check lightning payment status () 2024-09-17 21:41:04 +09:00
0f93581ff5 Refactor confirmation count tracking () 2024-09-17 17:28:58 +09:00
397452a7fe Setting standard 150x100 size for Unbank logo 2024-09-16 16:38:04 -05:00
cd3157361a Adding Unbank as BTCPay Server Foundation Supporter () 2024-09-16 16:33:11 -05:00
29a89f185a Fix: Not able to change SpeedPolicy of a store 2024-09-13 22:59:14 +09:00
2f7a5c2967 Wallet: Generate receive address automatically ()
* Wallet: Generate receive address automatically

This circumvents landing on a blank page with only the "generate address" button and automatically generates a new address, unless the Unreserve action was used.

* Fix close button leading to same page

* Fix tests

* Remove unreserve feature

---------

Co-authored-by: Nicolas Dorier <nicolas.dorier@gmail.com>
2024-09-13 22:03:45 +09:00
f07ed53f7e Handle password reset when SMTP isn't configured or validated ()
* Handle password reset when SMTP isn't configured or the configuration cannot be validated

* include rel in external a tag

* Simplify it

* Test fix

* Simplify a bit

* selenium test to manage users

---------

Co-authored-by: Dennis Reimann <mail@dennisreimann.de>
Co-authored-by: Nicolas Dorier <nicolas.dorier@gmail.com>
2024-09-13 21:42:08 +09:00
7348a6a62f Store Branding: Apply brand color to backend as well ()
* Store Branding: Apply brand color to backend as well

Closes .

* Add adjustments for different theme scenarios

* Add description text

* Make it optional to apply the brand color to the backend

* Toggle color fixes
2024-09-13 21:39:21 +09:00
b7ba53eb60 UI: Fix for truncate center component () 2024-09-13 17:55:31 +09:00
e389d6a96b UI: Minor fix on policies page 2024-09-12 18:43:52 +02:00
0238dffc7a POS: Fix accounting for manually entered keypad amounts ()
* POS: Fix accounting for manually entered keypad amounts

For keypad orders where there are products AND manual amount entries, we didn't account for the latter.

Fixes .

* Adjust wording: "Manual entry" becomes "Custom Amount"
2024-09-12 21:36:35 +09:00
666445e8f7 Greenfield: App endpoints for sales statistics () 2024-09-12 16:17:16 +09:00
36bada8feb Uniformize Wallet API's path ()
* Uniformize Wallet API's path

* Rewrite old API path to new API

* Rename routes
2024-09-12 15:19:10 +09:00
b4946f4db1 Fix divisibility in invoice details of lightning amounts ()
* Fix divisibility in invoice details of lightning amounts

This PR will show 11 decimal in the invoice details for BTC amount
of lightning payment methods.

It also hacks around the fact that some
lightning clients don't create the requested amount of sats, which
resulted in over or under payments. (Blink not supporting msats, and
strike)

Now, In that case, a payment method fee (which can be negative) called tweak fee
will be added to the prompt.

We are also hiding this tweak fee from the user in the checkout page in
order to not disturb the UI with inconsequential fee of 0.000000001 sats.

* Only show 8 digits in checkout, even if amount is 11 digits
2024-09-12 12:43:08 +09:00
f3d485da53 Invitation process improvements ()
* Server: Make sending email optional when adding user

Closes .

* Generate custom invite token and store it in user blob

Closes btcpayserver/app/#46.

* QR code for user invite

Closes .

* Text fix
2024-09-12 12:31:57 +09:00
3342122be2 Make Role Permissions more human legible ()
Had to rename `CanModifyStoreWebhooks` to `CanModifyWebhooks` for this, but the value stayed the same, so I don't think it's a big deal.

Closes .
2024-09-12 12:29:10 +09:00
f59751853a Fx pos data additional info ()
* Resolve Additional Information from posData

* code formatting

* Minor adjustments

* Update ChromeDriver

* Revert and improve PosData partial

---------

Co-authored-by: Dennis Reimann <mail@dennisreimann.de>
Co-authored-by: Nicolas Dorier <nicolas.dorier@gmail.com>
2024-09-12 12:27:02 +09:00
a60c55c6df Transactions: Improve TX ID display ()
* Transactions: Improve TX ID display

* Elastic fix

* Test and behaviour fix
2024-09-12 10:08:16 +09:00
3bad5883bb Permissions: Remove deprecated custodian account policies ()
Updates the store owner role and removes these three deprecated policies:

- `btcpay.store.cantradecustodianaccount`
- `btcpay.store.canwithdrawfromcustodianaccount`
- `btcpay.store.candeposittocustodianaccount`
2024-09-12 10:02:37 +09:00
d4c30866b7 Fix timeout issues during migration () 2024-09-10 20:11:07 +09:00
7c92ce771f Reindex Invoices table if corrupt, fix migration timeout () 2024-09-10 17:34:02 +09:00
4601359ebe Do not block the Lightning PendingPayoutListener if a client is failing 2024-09-10 17:33:36 +09:00
04b1130837 Fix tests 2024-09-09 23:39:59 +09:00
c377617b5a Fix migration issue on invalid json char 2024-09-09 23:15:53 +09:00
222e8f66df Fix entity half migrated entities for v2 2024-09-09 21:21:04 +09:00
87e2f5f414 Add migration logs 2024-09-09 19:03:07 +09:00
7de05700e9 Edit dictionary should be in JSON format () 2024-09-09 11:25:36 +09:00
841f41da2f Invoice: Improve zero amount invoice handling ()
This is for the checkout page to properly redirect paid invoices with no payment methods (e.g. free invoices with zero amount) to either the receipt page or redirect URL. Only fall back to 404 if there is neither.

Fixes .
2024-09-09 11:05:03 +09:00
73dcde7780 UI: Create Store CTA should be full-width
Closes 
2024-09-06 13:23:52 +02:00
377ff52222 Fix: Incorrect rounding for lightning amounts () 2024-09-06 18:48:20 +09:00
e4b5609d78 Fix doc 2024-09-06 13:27:12 +09:00
c93497af10 Rename PaymentMethod => PaymentMethodId ()
* Rename PaymentMethod => PaymentMethodId

* Rename DB Columns
2024-09-06 13:24:33 +09:00
99dda66bbc Rename PayoutData.Destination () 2024-09-06 10:34:10 +09:00
4ce68f817d UI: Spacing fixes for Policies page 2024-09-02 12:06:34 +02:00
1c027be106 Greenfield: Can set the label when generating a wallet for store () 2024-09-02 18:54:15 +09:00
4a94074595 Add topups to payouts () 2024-09-02 18:37:39 +09:00
b49f6c3f86 Checkout fixes and improvements ()
* Adjust to new payment method IDs

* Sound improvements

* NFC: Handle no data/empty tag case

Fixes .
2024-09-02 18:20:29 +09:00
9c8fe9277d Fix route's param in doc 2024-09-02 16:27:49 +09:00
a7c70e6912 Add missing doc for scriptPubKeyType TaprootBIP86 2024-09-02 10:35:06 +09:00
28156a5a6b Include hover to display total sales per day ()
* Include hover to display total sales per day

* Add label to top items as well

* Bring in recent Chart modifications from app

---------

Co-authored-by: Dennis Reimann <mail@dennisreimann.de>
2024-08-30 09:58:50 +02:00
a3cfb9e5e4 Removal of the Altcoins build ()
* Remove some useless #if ALTCOINS

* Removal of the Altcoins build
2024-08-30 08:34:23 +09:00
25ccc6a9f9 Update notificaiton counter record () 2024-08-29 12:47:29 +02:00
8d1904c185 Sales report dashboard issue - querySelectorAll returns multiple elements that leads to not updateding the reuired element innerHtml directy, so i change querySelector that will directly update our .sales-count element innerHtml ()
Co-authored-by: Umar Farooq <umar.u.farooq@constellationdealer.com>
2024-08-29 11:30:34 +02:00
85cc221d50 Remove obsolete errors 2024-08-29 10:47:03 +09:00
588b00de45 Fix potential crash in migration 2024-08-29 10:21:25 +09:00
656a1f8294 bump version 2024-08-29 09:40:22 +09:00
1dd37c5020 Refactor Payouts and PullPayments DB models () 2024-08-28 18:52:08 +09:00
3c40dc1f49 Delete columns CustomerEmail, OrderId, ItemCode () 2024-08-27 09:53:28 +09:00
6d560caf06 Small optimization of query () 2024-08-22 18:52:53 +09:00
07f3301e32 Remove unused code () 2024-08-22 18:52:41 +09:00
9410a293f7 Fix tests 2024-08-22 17:21:06 +09:00
a0704eddb0 Fix tests 2024-08-22 16:34:37 +09:00
672491c66c UI: Offcanvas fixes 2024-08-20 18:12:32 +02:00
bd91c45d1f Minor design update 2024-08-20 18:12:21 +02:00
f77cdb7148 Removing Strike as BTCPay Foundation supporter and cleaning up old SVGs () 2024-08-11 13:21:09 -05:00
7878a4365c Dictionary additions ()
* Cleanups

* Add text entries for dictionary pages

* Wording: Keep Clone title consistent with Payment Request cloning

* Dictionaries: List used one first; badge for marking In use
2024-07-26 08:46:17 +09:00
94760792af Add parts of the UI to translate () 2024-07-25 22:46:02 +09:00
50dafd2452 Uniform primary action id's of pages () 2024-07-25 08:23:28 +02:00
ca4abcb497 Allow translations of BTCPay Server Backend by admins () 2024-07-24 20:16:20 +09:00
acbc75d077 Fix xmr status message () 2024-07-19 21:46:24 +09:00
ce4d73fcad Changelog v1.13.5 2024-07-17 11:25:23 +09:00
8b20cd9082 Fix: Plugin Exception Handler didn't disabled plugin if crash was detected 2024-07-17 11:23:14 +09:00
fe01e4693f Fix: Kraken rate provider failing due to bid > ask 2024-07-17 11:10:58 +09:00
1506a2738a Update Changelog 2024-07-15 22:00:07 +09:00
0eca1ddbf1 Shopify: Cancel rather than close an order () 2024-07-15 21:57:12 +09:00
859a8d1a11 Shopify: Create invoice when the payment page opens () 2024-07-15 21:57:00 +09:00
8aaf77d4b4 Add changelog and bump 1.13.4 ()
* Add changelog and bump 1.13.4

* Update Changelog.md

---------

Co-authored-by: d11n <mail@dennisreimann.de>
2024-07-11 22:39:16 +09:00
10977e6750 Move AdditionalData to AppBaseData 2024-07-11 21:43:30 +09:00
66d8dda0f5 Timespan in API should be parsed with invariant culture 2024-07-11 21:41:19 +09:00
25ae6df095 Greenfield: Add file endpoints and upload ()
* Greenfield: Add file endpoints and upload

- Endpoints for server files
- File upload using `multipart/form-data`

Closes .

Can also be tested using cURL:

- `curl --location 'https://localhost:14142/api/v1/files' --header 'Authorization: token MY_API_TOKEN' --form 'file=@"LOCAL_FILEPATH"'`
- `curl --location 'https://localhost:14142/api/v1/users/me/picture' --header 'Authorization: token MY_API_TOKEN' --form 'file=@"LOCAL_FILEPATH"'`

* Revert UnresolvedUri changes

* Add upload for store logo
2024-07-11 09:28:24 +09:00
249b991185 Checkout: Display description if present ()
This is mostly for the POS, where either an individual item title might be the description, or in case of the cart and keypad, the name of the app will be displayed. The description is omitted if it matches the store name, to avoid duplicate titles.

Closes .
2024-07-11 00:14:18 +09:00
bc6d037341 POS: Improve padding on mobile and unify card look with tiles ()
On mobile, the description content was lacking horizontal padding. This adjusts it while also unifying the cards to work like the tiles on checkout: Below 400px width, we pull the to the edges of the screen, which makes it looks nicer and display better than as if they'd also have an outer margin.

Adjustments take effect on all POS view variants.
2024-07-11 00:12:58 +09:00
a4a1fa0746 Greenfield: Add store id for notifications ()
* Rename filter to storeid for consistency with other filters

* Greenfield: Add storeId to notification

* Cleanups

* Greenfield: Allow filtering notifications by store id
2024-07-11 00:12:22 +09:00
d73e26e0c4 Fix null pointer exception on receipt print page () () 2024-07-11 00:11:04 +09:00
372688b723 Disable plugins if they crash the Dashboard page () 2024-07-11 00:09:54 +09:00
a5ab68ab02 Greenfield: Fix missing delete app policy () 2024-07-10 22:50:32 +09:00
e7297ce6b8 Truncate Center Component: Fix Vue rendering ()
* Truncate Center Component: Fix Vue rendering

* Simplify the TruncateCenter component

---------

Co-authored-by: nicolas.dorier <nicolas.dorier@gmail.com>
2024-07-10 11:00:49 +02:00
a88b39e50d Fix missing normalization of Settled invoice status ()
Addition to , which fixes lookups for invoices with status `Settled` (e.g. for Sales tiles on the Dashboard).
2024-07-10 15:33:48 +09:00
a0988c250d Handle LNURL Payouts failing due to amount restriction ()
* Handle LNURL payouts better when amount is not allowee

* docker-compose: Add missing restart for merchant CLN container

* show sats not msats

---------

Co-authored-by: Dennis Reimann <mail@dennisreimann.de>
2024-07-10 11:05:25 +09:00
3a52759090 Bump EF libs () 2024-07-10 08:50:43 +09:00
ddb07a7ba9 Receipt page fixes ()
* Receipt: Don't assign empty values to data; hide present empty values
* Receipt: Use same URL on "Return to Store" link as on invoice
2024-07-09 16:59:36 +02:00
b5ad5a5f6f TransactionLinkProviders: Don't force single item ()
* TransactionLinkProviders: Don't force single item

Fixes .

* docker-compose: Add missing restart for merchant CLN container
2024-07-09 16:19:08 +02:00
a46073f3e4 Fix email settings on store level, when server has no email settings ()
* Fix email settings on store level, when server has no email settings

Fixes .

* docker-compose: Add missing restart for merchant CLN container
2024-07-09 16:16:53 +02:00
f03ea5c115 Bumping LND to 0.18.1-beta () 2024-07-07 07:48:02 -05:00
f7569b715d Greenfield: Add missing invoice metadata to Lightning Address ()
* Greenfield: Add missing invoice metadata to Lightning Address

Closes .

* Update BTCPayServer/wwwroot/swagger/v1/swagger.template.stores-lightning-addresses.json
2024-07-04 22:42:02 +09:00
6e19d21575 Reports: Fix dropdown z-index 2024-07-04 13:22:08 +02:00
4eb155696c Fix Fake Data Generator for the reports 2024-07-04 19:01:58 +09:00
1157e2d04b Fix: Reports rows weren't always properly sorted (Fix ) 2024-07-04 18:09:30 +09:00
247532e3c4 Do not crash when refunding an invoice that has been marked settled (Fix ) () 2024-07-04 16:43:30 +09:00
4a2f61de9f Fix crash if refunding an invoice without payment 2024-07-04 10:43:39 +09:00
654cf1982e Icon fix in checkout CSS 2024-07-03 14:13:29 +02:00
e0a0406825 Improved Notifications List View (, ) 2024-07-02 17:55:54 +09:00
d6d14b4170 Re-add missing file upload
This was accidentally removed in .
2024-07-01 11:52:10 +02:00
cbeb32808a Dashboard: Remove View All link for Top Items ()
Fixes .
2024-06-29 11:44:51 +02:00
a295e123bc Migrate Payouts to new format ()
* Migrate Payouts to new format

* Rename PayoutData column to PayoutMethodId
2024-06-28 20:07:53 +09:00
c56b660c92 bump selenium container () 2024-06-28 17:32:55 +09:00
6a5bc99a55 Color fix 2024-06-27 16:39:13 +02:00
833895e797 Design system updates; add icons 2024-06-27 10:43:28 +02:00
fa080ce0a4 Add AdditionalData to PointOfSaleBaseData 2024-06-26 17:43:56 +09:00
2482b9df74 Greenfield: Refactor app endpoints ()
* Greenfield: Refactor app endpoints

- Do not change unset data
- Clean up difference between request (template) and data (items/perks)
- Add missing properties (form id and custom tip percentage)
- Update docs

* Revert ToSettings changes in GreenfieldAppsController
2024-06-26 17:42:22 +09:00
bf66b54c9a User: Add name and image URL ()
* User: Add name and image URL

More personalization options, prerequisite for .

Additionally:
- Remove ambigious and read-only username from manage view.
- Improve email verification conditions and display.
- Greenfield: Update current user. Prerequisite for .

* Refactor UpdateCurrentUser

* Replace new columns by UserBlob

* Update email check and add test case for mailbox addresses

---------

Co-authored-by: nicolas.dorier <nicolas.dorier@gmail.com>
2024-06-26 17:39:22 +09:00
1ba7b67e70 Add setup script for dev basics like users and stores ()
* Add setup script for dev basics like users and stores

I'm using and extending this script for setting up the basics after I erase my dev containers. Once you clean out everything with `cd BTCPayServer.Tests && docker-compose down --volumes --remove-orphans && docker-compose up dev` you otherwise have to recreate everything manually. This gives you ...

- An admin user with unrestricted API key
- One additional user per default role
- Store 1: Satoshis Steaks with Hot Wallet and Internal Node (and all users assigned to that store)
- Store 2: Nakamoto Nuggets with Hot Wallet and Merchant LND Lightning node (and all users besides Guest assigned to that store)
- Nakamoto Nuggets also gets Cart and Keypad apps
- Store 3 with External Lightning based store with Customer LND Lightning node

## Sample output

```bash
Admin ID: 78aa0b35-6c72-45ac-a7d4-b7976ebbbb62
Admin API Key: 992023ae659295b14c3b429007bbf67c2fec057d

Store Owner ID: e3151462-b0f8-4342-879e-16e42d3432d9
Store Manager ID: d0f11a4d-7c9f-466d-bbb9-dfba09295446
Store Employee ID: 13a882de-65f1-4be9-819e-be058e54a8a9

Satoshis Steaks Store ID: FDyaDcDxtSnNx77nEtT8VL55tcitcrV3Zoj5B6eoByEL

Nakamoto Nuggets Store ID: 8uJqvtPnvCU1XXBiSNBLGEn5XinwC1qYcyP495pPzn9a
Nakamoto Nuggets Keypad POS ID: 2q3Z6b8RUfwrvMyYngNAbj8kPqU8
Nakamoto Nuggets Cart POS ID: 2TfyrzZjiWnYp9QwWyy4U7y5dAFP

External Lightning Store ID: Cr56Ch7h3cgGPcbsZnKWnqCisMojfAdUVJhsN3zLcqSP
```

* Fix path issue
2024-06-26 09:56:22 +09:00
feffbbd980 Greenfield: Manage notifications ()
Prerequisite for .
2024-06-25 20:01:11 +02:00
e0c5ac5271 Fix Monero development environment with wallet load () 2024-06-24 15:16:11 +02:00
50656dc7d3 UI unifications 2024-06-23 19:38:34 +02:00
cbeea86a13 Do not crash the plugin packer if Assembly.GetTypes crashes 2024-06-21 10:16:16 +09:00
5a6bdd756a POS: Keypad error should sit below the header ()
Fixes .
2024-06-20 16:17:36 +02:00
cf7bb8cff7 Fix tests ()
* docker-compose: Add missing restart for merchant CLN container

* Upgrade ChromeDriver

* Fix path and test
2024-06-20 10:48:23 +02:00
b898fd0ec1 TimeSpan JSON Converter: Parse strings ()
Something I came across while working in the app: Without this, the [TimeSpan default values of the StoreBlob](https://github.com/btcpayserver/btcpayserver/blob/master/BTCPayServer/Data/StoreBlob.cs#L84) cannot be parsed and don't get applied properly.
2024-06-19 15:31:13 +02:00
f8f98ab7f0 BTCPayServerClient refactoring ()
* Allow overrides on all methods

* Add non-returning SendHttpRequest methods

* Use SendHttpRequest consistently

* Use file-scoped namespace

* Rates: Set default null value for currencyPair

* Ensure it works with instances which have BTCPAY_ROOTPATH set
2024-06-19 15:25:33 +02:00
0f8da123b8 UI: Move section navigation to sidebar ()
* UI: Move section navigation to sidebar

* Scroll active nav link into view

* Move CTAs to top right

* Server Settings: Make Policies first page

* Responsive table fixes

* Spacing fixes

* Add breadcrumb samples

* store settings fixes

* payment request fixes

* updates pull payment title

* adds invoice detail fix

* updates server settings breadcrumbs + copy fix

* Don't open Server Settings on Plugins page

* Add breadcrumbs to pull payment views

* adds breadcrumbs to account

* server and store breadcrumb fixes

* fixes access tokens

* Fix payment processor breadcrumbs

* fixes webhook 404

* Final touches

* Fix test

* Add breadcrumb for email rules page

* Design system updates

---------

Co-authored-by: dstrukt <gfxdsign@gmail.com>
2024-06-19 15:23:10 +02:00
aa9e1acb91 Fix Monero local dev docker start up () 2024-06-18 09:57:54 +02:00
61e4995c14 bump v 2024-06-13 14:49:36 +02:00
078ce28b8a v1.13.3: Add Changelog ()
* v1.13.3: Add Changelog

* Update Changelog.md

---------

Co-authored-by: Andrew Camilleri <evilkukka@gmail.com>
2024-06-13 14:49:29 +02:00
88fe28df8d bump cln 2024-06-12 19:39:23 +02:00
e14b055651 Pull payment: Enable CORS for LNURL request ()
Fixes  and ensures Mutiny Wallet can do the LNURL request.
2024-06-12 11:13:51 +02:00
e5a09cbeaa POS: Don't show free items in print view ()
We cannot generate a proper LNURL response for free items, hence we should not show them in the print view.

Closes .
2024-06-05 22:11:26 +09:00
b5ade89763 fix shopify 2024-06-05 08:53:40 +02:00
2c63d16774 Refactor shopify logic ()
This refactors the logic around shopify to keep it in one place. invoice Statuses are handled in a more streamlined way.
2024-06-05 09:00:55 +09:00
36fcde29ae Update changelog 2024-06-03 22:03:35 +09:00
c5aca1b7f7 Cancel shopify order when invoice payment fails ()
* Cancel shopify order when invoice payment fails

* void correctly and invoice logs
2024-06-03 22:02:27 +09:00
4307fa24a7 Bumping LND to 0.18-0-beta () 2024-06-01 19:28:41 -05:00
d8d36cf4c7 Fix: Adding a label to a base58 addresses in the Send Wallet screen wasn't working () 2024-05-27 23:16:51 +09:00
1374213308 Add changelog for 1.13.2 () 2024-05-24 16:13:18 +09:00
31a9466bf8 Dashboard: Add table-responsive wrapper for transactions and invoices ()
Fixes .
2024-05-24 14:11:05 +09:00
181e4fd2fc Sanitizer: Allow bitcoin and lightning URI schemes ()
Fixes .
2024-05-24 14:10:32 +09:00
03ba6b39ec Bump dependencies () 2024-05-23 22:16:16 +09:00
c063c70b07 Search: Display text filters in search input ()
* Search: Display text filters in search input

This changes the search text input to also display the filters, which don't have a special UI (e.g. dropdown). Those filters (e.g. orderid) were not displayed before and hence could not be reset.

Fixes .

* Add and fix test
2024-05-23 20:22:16 +09:00
89fcf86bb4 POS: Allow overpay for articles with minimum price ()
Adjust the LNURL settings so that minimum price items aren't capped and can be overpaid.

Also fixes the missing LNURL response for free items to return a proper LNURL error instead of just 404.

Fixes .
2024-05-23 20:20:58 +09:00
faf6b514e6 Fix error message if rate unavailable 2024-05-21 22:11:03 +09:00
7564c3c2bd Fix: Some valid taproot PSBT couldn't parsed and show better error message (Fix ) () 2024-05-21 10:52:55 +09:00
fc9d4f96a7 Design system and icon updates for 2.0 ()
* Design system updates

* Icon fix

* Add new icons, replace show/hide

* Icon replacements

* Test fix

* Icon replacements in Vault

* More icon replacements

* Final icon replacements, remove Font Awesome
2024-05-20 08:57:46 +09:00
c6d46fcdd0 Minor refactorings 2024-05-17 14:48:55 +09:00
8331d1019f Can hide the footer of LayoutSimple 2024-05-17 09:28:00 +09:00
900c5f7976 Show boltcard deeplink also on iOS 2024-05-17 09:10:29 +09:00
d2a04db49f Cutting off lightning payment so receipt takes less space 2024-05-16 09:41:28 +09:00
7793c5e5df Allow user to input a passphrase for Trezor v1 (Fix ) () 2024-05-15 09:18:20 +09:00
69e0ae76c7 Fix: Error while connecting to websockets without reverse proxy () 2024-05-15 09:17:45 +09:00
c134602cbd Remove Legacy Status from the code () 2024-05-15 07:49:53 +09:00
d96b066658 Recommended exchange to be resolved during Invoice Creation ()
* Recommended Exchange Rate Selection during Invoice Creation

* Make Recommended exchanges pluginifiable
2024-05-13 22:29:42 +09:00
b0da802abe Fix broken build 2024-05-10 22:18:57 +09:00
2c4d87aef8 Fix migration crash on postgres above 13.10 2024-05-10 22:13:08 +09:00
11b38a7a4c allow admin update default rate provider and default currency for stores ()
* allow admin update default rate provider and default currency for stores

* Updated PR on allowing server admin to update currency

* update server settings to select default currency

* revert standard default currency

* clean up

* Minor rewording

---------

Co-authored-by: nicolas.dorier <nicolas.dorier@gmail.com>
2024-05-10 15:26:04 +09:00
84df96429c Refactor of Default RateRules 2024-05-10 08:55:53 +09:00
adbe5977cd Decouple DefaultRates from BTCPayNetwork () 2024-05-09 17:20:24 +09:00
4c303d358b Branding updates for 2.0 ()
* Remove deprecated CSS options

Closes .

* Greenfield: Add brandColor to store APIs

Closes .

* Migrate file IDs to URLs

Closes .

* Greenfield: Add CSS and logo URL to store settings API

Closes .

* Add migration test

* Store and Server branding can reference file's via fileid:ID

* Add PaymentSoundUrl to Store API

---------

Co-authored-by: nicolas.dorier <nicolas.dorier@gmail.com>
2024-05-09 09:18:02 +09:00
eba3475a1b Theme docs link fix ()
We recently removed the section the anchor links to and we'll remove the links entirely in .
2024-05-08 09:11:04 +09:00
8bb4ceaaac Remove dead code from multi db support () 2024-05-07 09:21:49 +09:00
a89c0d4797 Add refund reports ()
* Add refund reports

* Fix fake data generator in reports
2024-05-06 18:44:16 +09:00
a8e16b0ba6 Wallet UI quick wins ()
- Unify single/multiple inputs display
- Destination: Move labels above amount
- Coin Selection: Add sorting by amount and confirmations, add page size. Closes 
- Move PSBT and BIP21 buttons up to input related button group
- Turn checkboxes in Advanced Settings into toggles
- Improve spacings and button groups
2024-05-06 14:40:17 +09:00
7de4e8b001 Show better error message for invalid destination in PullPayments () 2024-05-05 21:03:25 +02:00
b4cd74056e Remove period concept from PullPayment () 2024-05-01 17:59:10 +09:00
9db9c5e936 Decouple PaymentMethodId from PayoutMethodId () 2024-05-01 10:22:07 +09:00
247afe6a7b Fix launchsettings 2024-04-30 20:12:11 +09:00
90bc087ad6 Fix test 2024-04-30 18:41:08 +09:00
6049fa23a7 Support pluginable rate providers ()
* Support pluginable rate providers

This PR allows plugins to provide custom rate providers, that can be contextual to a store. For example, if you use the upcoming fiat offramp plugin, or the Blink plugin, you'll probably want to configure the fetch the rates from them since they are determining the actual fiat rrate to you. However, they require API keys. This PR enables these scenarios, even much more advanced ones, but for example:
* Install fiat offramp plugin
* Configure it
* You can now use the fiat offramp rate provider (no additional config steps beyond selecting the rate source from the select, or maybe the plugin would automatically set it for you once configured)

* Apply suggestions from code review

* Simplify

* Do not use BackgroundFetcherRateProvider for contextual rate prov

---------

Co-authored-by: nicolas.dorier <nicolas.dorier@gmail.com>
2024-04-30 18:31:15 +09:00
4821f77304 Guard against running current master ()
* Guard against running current master

With a longer release cycle for v2, we need to guard people from running master and corrupting their data. This adds a new requirement in that  a special config must be set when running master. We will remove when v2 rc is ready.

* add envs
2024-04-30 18:29:05 +09:00
c348f442cc Checkout: Fix copying numeric values to clipboard ()
Fixes .
2024-04-30 18:26:27 +09:00
c23fab5b34 Greenfield API clarifications, fix typo subscriptions -> registrations () 2024-04-30 18:22:41 +09:00
8d429f064b Show Lightning node availability in navigation ()
* Show Lightning node availability in navigation

Instead of simply communicating the setup state of the store's LN node, this now also checks its availability.

Closes  .

* Cleanups

* Add Selenium test for public node page and status in nav

* Cache the available lightning node result

---------

Co-authored-by: nicolas.dorier <nicolas.dorier@gmail.com>
2024-04-26 08:30:34 +02:00
d3277306cf Avoid timeouts during EF migrations () 2024-04-25 17:27:45 +09:00
4e0423cb1e Disable mempool fee test 2024-04-25 14:16:03 +09:00
0f08d3e3a3 Remove migrations prior to 1.0.3.162 ()
* Consolidate EF migrations up to 03/2020 into a single SQL script

* Remove old migrations code
2024-04-25 14:09:21 +09:00
0c35939001 Cleanup data from the InvoiceEvents table () 2024-04-25 14:09:01 +09:00
06edb0e157 Server email settings: Fix missing password field ()
Fixes .
2024-04-24 23:02:54 +09:00
c0f9716b1e bump NTag424 lib 2024-04-24 21:50:18 +09:00
d10e07b67b Pull payment QR scan fixes ()
Updates the icon and fixes a JS error, in case the LNURL/Boltcard option isn't available.
2024-04-24 21:05:35 +09:00
22cf253183 Remove some legacy code 2024-04-24 17:24:15 +09:00
56d57bbd84 Improve data display on receipt ()
Once more an improvement for the receipt, which also fixes :

- Unify data displayed on the web and print version
- Split cart and additional data and ensure additional data is displayed
- Do not display extra subtotal row if there are no tips or discounts
- Make PosData partial more universal and backwards-compatible by using case insensitive key lookups
2024-04-24 10:22:00 +02:00
aeb836da76 Domain mapping constraint: Fix .onion case ()
Fixes , which is a regression introduced in . The Tor-check must happen to prevent redirecting, but we must still return true if we already resolved an `appId`.
2024-04-24 11:24:00 +09:00
e4440ca0dc Fix node public info 2024-04-24 10:14:11 +09:00
8c4cf779ce fix bolt 11 0 amount check for payouts ()
Co-authored-by: d11n <mail@dennisreimann.de>
2024-04-23 09:36:49 +02:00
c8f003950a Remove debug logs 2024-04-22 10:54:44 +09:00
f4aafd5be3 Cleanup AddressesInvoices table () 2024-04-16 16:18:56 +09:00
bd978f29a0 Fix flaky tests () 2024-04-16 12:48:42 +09:00
76719cdc4a Greenfield: Fix payment method update regression ()
* Greenfield: Fix payment method update regression

Do not exclude if `enabled` is `true`. Got introduced in .

* Fix PaymentMethodId Swagger docs
2024-04-16 11:32:52 +09:00
36cfc3648f bump 2024-04-15 22:26:50 +09:00
7e80fd2e98 Prevent payout double send ()
Fixes .
2024-04-15 13:25:17 +02:00
f1a04a3bd0 Remove MySQL and Sqlite deps () 2024-04-15 19:08:25 +09:00
9699b3837b Bump CLightning () 2024-04-15 18:22:30 +09:00
d2e95c2246 update csv export to include full date and time in 12 hour format ()
* update csv export to include full date and time in 12 hour format

* formatting the export datetime to 24 hours
2024-04-15 12:21:56 +09:00
893b92439c Fix: Unable to use a different postgres schema (Fix ) 2024-04-15 11:38:32 +09:00
8819372d2e Small payment request fixes ()
* Do not crash payment request page on 0 amount

* set email from form to payment request
2024-04-12 12:56:11 +02:00
b9cea968e5 (bug) handle null XMR settings ()
Co-authored-by: Henry Hollingworth <henry.hollingworth@alcoa.com>
2024-04-12 07:51:56 +02:00
6846a7cef5 Fix JSContent test 2024-04-08 21:32:55 +09:00
eee3fb8b5f Merge pull request from NicolasDorier/fweohtqn
Remove CheckoutV1
2024-04-06 11:55:36 +09:00
6245ecc8f4 Fix tests, remove duplicate tests 2024-04-05 18:56:16 +02:00
d2e9ec9494 Cleanup v2 leftovers 2024-04-05 18:05:51 +02:00
4208110d57 Remove CheckoutV1 2024-04-05 16:58:13 +09:00
ff5e03a8b1 Merge pull request from btcpayserver/fweoinqrnt
Fix migration crash
2024-04-04 23:11:21 +09:00
57851cef9a Fix migration crash 2024-04-04 23:10:58 +09:00
e7cb3c0a85 Merge pull request from dennisreimann/store-controller-refactoring
Refactoring UIStoresController
2024-04-04 18:21:02 +09:00
620ebc751c Convert to file-scoped namespace 2024-04-04 11:00:18 +02:00
c7a8523b77 Code cleanups 2024-04-04 10:58:47 +02:00
df4d370524 Rename and clean up properties 2024-04-04 10:48:09 +02:00
71ba5d9c4c Move actions and methods to separate partial controllers 2024-04-04 10:48:07 +02:00
d216ad7da9 fix: switch to using get_info for monerod () 2024-04-04 17:38:41 +09:00
6cc1751924 The Big Cleanup: Refactor BTCPay internals () 2024-04-04 16:31:04 +09:00
69b589a401 Adding Tether as BTCPay Server Foundation Supporter ()
* Adding Tether as BTCPay Server Foundation Supporter

* Adding Tether to _BTCPaySupporters partial as well

* Modfying supporter_strike.svg to have white backgroundf or dark mode

* Modifying supporter_tether.svg to fit in the 150x100 box

* Centering Tether shape
2024-04-02 19:06:24 -05:00
db73b1f268 Fix test CheckJSContent 2024-04-01 12:11:00 +09:00
9ac0e982d6 chore: fix typos () 2024-03-30 10:20:24 +01:00
cb25c225e9 Remove custodians ()
* Remove custodians

* Hide Experimental checkbox in the server policies

---------

Co-authored-by: Nicolas Dorier <nicolas.dorier@gmail.com>
2024-03-29 00:28:27 +09:00
5b31d4de20 v1.13: Update changelog () 2024-03-28 22:40:18 +09:00
14f8c73b08 POS: Increase size of quantity buttons ()
Closes .
2024-03-28 09:01:56 +09:00
529075f64c Make "Employee" default role on store settings ()
* Refactoring: Use property rather than injecting StoreRepository

* Update info text

* Make "Employee" default role on store settings

Closes .
2024-03-28 09:01:34 +09:00
dba102e74f Template Editor: Fix mobile view ()
Fixes .
2024-03-27 19:20:49 +09:00
0f3f8b6bf9 Contact Us improvements ()
* Add contact link to sidebar

Closes .

* Obfuscate contact email on public pages

Closes .

* Fix
2024-03-27 19:19:39 +09:00
83028b9b73 Adding introduction, Authentication and Usage examples sections to the API docs. () 2024-03-24 00:02:01 +09:00
1fe766cb16 Keypad: Fix images () 2024-03-22 15:16:59 +01:00
6b45eb0d3d Do not throw when local node is not synced and using external ln node ()
* Do not throw when local node is not synced and using external ln node

* Fix additional bug when ln conn strings without server would crash
2024-03-22 10:06:38 +01:00
6b0087ab69 Update version 2024-03-21 21:40:09 +09:00
e60fd8d6ab Changelog v1.13 () 2024-03-21 21:39:06 +09:00
88a1d83323 Support bbqr psbts ()
* Support bbqr psbts

https://bbqr.org/ @nvk

* add js test for bbqr
2024-03-21 10:30:23 +01:00
e21a8df0f3 smaller printed receipts () 2024-03-21 17:30:34 +09:00
93f37b506b UI: Improve Create First Store view ()
Unifies the width and display with the login view.
2024-03-21 07:37:15 +01:00
fca3480e37 Specify mailto: prefix for emails in Server Settings ()
* Specify mailto: prefix for emails in Server Settings

* resolve test failure

* Update wording

* Apply mailto-prefix on setting change

---------

Co-authored-by: Dennis Reimann <mail@dennisreimann.de>
2024-03-19 15:04:09 +01:00
966547db54 Template Editor: Apply item changes directly ()
Closes .
2024-03-19 14:59:26 +01:00
09dbe44bca Onboarding: Invite new users on store level ()
* Onboarding: Invite new users

- Separates the user self-registration and invite cases
- Adds invitation email for users created by the admin
- Adds invitation tokens to verify user was invited
- Adds handler action for invite links
- Refactors `UserEventHostedService`
- Fixes .

* Add permissioned form tag helper

* Better way of changing a user's role

* Test fixes
2024-03-19 14:58:33 +01:00
b7ce6b7400 Providing additional parameter for info message ()
* Providing additional parameter for info message

* Refactoring code to remove parameter and only set status message in LoadFromBIP21 if not present

* Update BTCPayServer/Controllers/UIWalletsController.cs

---------

Co-authored-by: Andrew Camilleri <evilkukka@gmail.com>
2024-03-18 12:17:30 +01:00
78f169cd24 chore: remove repetitive words ()
chore: remove repetitive words

Signed-off-by: soonsouth <cuibuwei@163.com>
2024-03-18 10:49:07 +01:00
d0e11f1ec4 changing check box to toggle in various setting views ()
* Resolves: check box to toggle in various setting views

* resolve conflicts

* Notification logic reversal

* remove transform property in the toggle

* Handle email tls certificate check

* Unifications and fixes

---------

Co-authored-by: Dennis Reimann <mail@dennisreimann.de>
2024-03-14 15:16:48 +01:00
912a706de9 Make Payouts and PullPayments columns JSONB () 2024-03-14 11:13:26 +01:00
9b5c8a8254 POS: Add item list to keypad ()
* Add admin option to show item list for keypad view

* Refactor common POS Vue mixin

* Add item list to POS keypad

* Add recent transactions to cart

* Keypad: Pass tip and discount as cart does

* Keypad and cart tests

* Improve offcanvas button

---------

Co-authored-by: Andrew Camilleri <evilkukka@gmail.com>
2024-03-14 11:11:54 +01:00
e5adc630af If pull payment opened in mobile, use deeplink to setup card ()
* If pull payment opened in mobile, use deeplink to setup card

* Allow passing LNURLW to register boltcard

* debug

* debug

* debug

* Only show setup/reset when the page is fully loaded

* Apply suggestions from code review

---------

Co-authored-by: Andrew Camilleri <evilkukka@gmail.com>
2024-03-14 11:07:11 +01:00
c56c6401d6 (feat) monero settlement thresholds ()
* (bug) treat xmr wallet directory as required

The wallet directory configuration setting is required
because the `UIMoneroLikeStoreController`'s
`GetMoneroLikePaymentMethodViewModel` method checks if the wallet file
exists, and to do that in needs the directory.

* (feat) xmr settlement thresholds

Adds the ability to select zero, 1, 10, or a custom number of
confirmations as the payment settlement threshold.

* (review) fix validation message not showing

---------

Co-authored-by: Henry Hollingworth <henry.hollingworth@alcoa.com>
2024-03-14 10:31:27 +01:00
0e64df3bbf Parallel payout ln () 2024-03-14 10:29:14 +01:00
e497903bf4 Support Admin being able to view stores ()
* Support Admin being able to view stores

* fix null check

* Delete obsolete empty view

* Add test

* Apply CanViewStoreSettings policy changes

Taken from 

* Fix Selenium tests

* Update dashboard permission requirement

---------

Co-authored-by: Dennis Reimann <mail@dennisreimann.de>
2024-03-14 10:25:40 +01:00
f1ff913cbe PoS app to show POS view for easy setup ()
* PoS app to show POS view for easy setup

* update selenium test

* Updates

* Add QR code icon

---------

Co-authored-by: Dennis Reimann <mail@dennisreimann.de>
2024-03-14 08:52:33 +01:00
3a00d32ce0 Fix flaky 2024-03-13 22:51:55 +09:00
f0f698f411 Fix tests (Fix ) 2024-03-13 22:29:39 +09:00
22c6468a5d Wallet: Label filter dropdown ()
Uses a dropdown component for the label filter, which also work on mobile. Closes .
2024-03-12 10:48:37 +01:00
a60072a431 do not have report name conflict with old plugin ()
* do not have report name conflict with old plugin

* tryadd instead of add

* Apply  to crowdfund too
2024-03-11 14:18:47 +01:00
dcc6f17c9c POS: Fix exception when asking for data with a top up item ()
Fixes .
2024-03-11 11:05:44 +01:00
15ce148b99 Apps: Make app name the default title ()
* Apps: Make app name the default title

Successor of  with a way simpler approach. Allows the user-facing title to be set, but defaults it to the app name instead of "Tea shop".

* Test fixes
2024-03-11 11:04:41 +01:00
3b73d5a5cb bump client version 2024-03-08 19:56:04 +09:00
1fd3054006 Fix: Old payments not showing up in reports () 2024-03-05 16:10:54 +09:00
2db1434929 Fix currency-api link () 2024-03-04 10:10:30 +09:00
a171671fe5 Update .NET 8.0 in vscode launch.json () 2024-03-01 22:07:34 +01:00
9160a1d71e Store Logo: Remove restriction of square dimension ()
As discussed on , there is no need for the store logo to be provided in square dimension. As it populates its own row on the public page layouts, we can remove that restriction and make it adaptable by providing only maximum height and width.
2024-02-29 09:34:28 +01:00
a896560a3c Lightning Setup page fixes ()
* Lightning Setup: Fix missing headline

Fixes .

* Lightning Setup: Fix tab switching UI glitch

Fixes .
2024-02-29 06:56:34 +01:00
e43b4ed540 Onboarding: Invite new users ()
* Server Users: More precise message when inviting users

This lets the admin who invited a new user know whether or not an email has been sent. If the SMTP server hasn't been set up, they need to share the invite link with the user.

* Onboarding: Invite new users

- Separates the user self-registration and invite cases
- Adds invitation email for users created by the admin
- Adds invitation tokens to verify user was invited
- Adds handler action for invite links
- Refactors `UserEventHostedService`

* Remove duplicate status message from views that use the wizard layout

* Auto-approve users created by an admin

* Notify admins via email if a new account requires approval

* Update wording

* Fix update user error

* Fix redirect to email confirmation in invite action

* Fix precondition checks after signup

* Improve admin notification

Send notification only if the user does not require email confirmation or when they confirmed their email address. Rationale: We want to inform admins only about qualified users and not annoy them with bot registrations.

* Allow approval alongside resending confirm email

* Use user email in log messages instead of ID

* Prevent unnecessary notification after email confirmation

* Use ApplicationUser type explicitly

* Fix after rebase

* Refactoring: Do not subclass UserRegisteredEvent
2024-02-28 20:43:18 +09:00
8b446e2791 Reposition the camera scan icon in the wallet > send functionality ()
* Reposition the camera scan icon in the wallet > send functionality

* refactored changes

* Minor adjustments

---------

Co-authored-by: Dennis Reimann <mail@dennisreimann.de>
2024-02-28 10:41:17 +01:00
d72b0e4cee Merge pull request from NicolasDorier/warnignremov
Remove disclaimer for lightning being in experimentation
2024-02-24 21:33:26 +05:00
22996ea21e UI: Deprecate the custom CSS options ()
* UI: Deprecate the custom CSS options

As discussed with @pavlenex we are deprecating the custom CSS options for particular entities (payemnt request, pull payment, POS, crowdfund) in favor of the store branding approach.

This displays the custom CSS section only if these values are set already.

* Add IDs to html element

This will allow styling of individual entities.
2024-02-23 13:42:00 +01:00
d55770cc16 Admin overview of the stores on the instance ()
* Admin overview of the stores on the instance

POC/Draft for .

* Enable admin to access foreign stores

* Remove stores list link

* UI updates

* Grant admins guest access to foreign stores

* Optimize cookie auth handler

* Test fix

* Revert changes related to StoreRepository.FindStore with isAdmin
2024-02-23 09:51:41 +01:00
5c98ca180a Webhook tests + FIXES + DOCS ()
* webhook tests

* fixes and add docs

* Do not update FormResponse and StoreId in update/create PullPayment

---------

Co-authored-by: nicolas.dorier <nicolas.dorier@gmail.com>
2024-02-23 09:44:42 +01:00
10bb75ce0e Add debug flaky test statement 2024-02-22 19:08:01 +09:00
b9e3686fcf Fix build warnings and flaky tests ()
* Make checkout v2 selenium tests more robust

* Fix build warnings

* Make payjoin test more robust

* Make LNURL test more robust
2024-02-22 09:38:06 +09:00
4ae1046571 Server Settings: Customize instance name and add contact URL ()
* Server Settings: Customize instance name and add contact URL

- The custom instance name would improve 
- Added contact URL closes 

* Fix custom logo display
2024-02-21 20:54:39 +01:00
147c6c4548 HTML Sanitizer updates ()
* Update HTML sanitizer package

* Remove unused sanitizer from apps

* Allow mailto: links

Fixes .
2024-02-21 20:53:24 +01:00
354338180b Store: Move support URL to Checkout Appearance and improve wording ()
As discussed in the recent design meeting.
2024-02-21 18:50:38 +01:00
5939e19f72 Lightning: Replace user info in server URL when logging ()
* Lightning: Replace user info in server URL when logging

Fixes .

* Fix empty user info case
2024-02-21 14:45:05 +01:00
f72a6df55a Add legacy report () 2024-02-21 14:44:49 +01:00
9c95b98f3a Policies: Cleanup and improvements ()
* Policies: Turn checkboxes into toggles

* Move email policy to server email settings

* Move maintenance policies to server maintenance settings

* Policies: Adjust spacings

* Policies: Remove DisableInstantNotifications setting

* Wording updates

* Move maintenance settings back
2024-02-21 14:43:44 +01:00
55a8ba0905 Adding link to API usage examples in docs. () 2024-02-21 14:42:15 +01:00
04037b3d2d Crowdfund : Add Buyer information / Additional information(forms) like POS ()
* Crowfund : Add Buyer information / Additional information(forms) like POS

* PR 5659 - changes

* Cleanups

* fix perk

* Crowdfund form tests

* Add Selenium test for Crowfund

* Selenium update

* update Selenium

* selenium update

* update selenium

* Test fixes and view improvements

* Cleanups

* do not use hacky form element for form detection

---------

Co-authored-by: nisaba <infos@nisaba.solutions>
Co-authored-by: Dennis Reimann <mail@dennisreimann.de>
Co-authored-by: Kukks <evilkukka@gmail.com>
2024-02-21 14:41:21 +01:00
4943c84655 Invoice: Improve events display ()
Closes .

- Adds seconds to the displayed date and time
- Adds a tooltip that displays the full date and time including milliseconds
- Reintroduced the colored text in case of unusual events/states (this didn't work before)
2024-02-21 14:08:28 +01:00
42a8160768 UI: Make store selector list scrollable if necessary ()
Fixes .
2024-02-21 13:34:47 +01:00
33d3a25928 Apps: Don't redirect .onion requests to canonical domain ()
Fixes .
2024-02-21 13:34:12 +01:00
c2acff81c6 Fix: Labels wouldn't be properly applied to some wallet's transactions () 2024-02-20 18:42:38 +09:00
214d4b0c3f Support 16mb psbts. Potentially fixes 2024-02-19 14:14:41 +01:00
bd4cf61c2b potentially fix 2024-02-19 13:01:08 +01:00
b592ee2fed Bumping LND to 0.17.4-beta ()
* Clarifying that only onchain funds will be restored to the wallet

Off chain recovery would need to be done with channel.backup file which is not part of this process

* Adding powershell version of lncli invoker

* Bumping LND to 0.17.4-beta-rc1

* Bumping LND to 0.17.4-beta
2024-02-16 08:43:51 -06:00
c57e1cca25 Shopify: Improve instruction display () 2024-02-16 09:36:41 +01:00
335f345ce3 Update GeneralSettings.cshtml ()
Removed trailing data
2024-02-13 09:48:16 +01:00
b7be93c569 Update NTag424 lib 2024-02-08 19:12:14 +09:00
cd01a7b727 Improve performance of payout db queries 2024-02-08 16:44:03 +09:00
b96e73a002 Fix: Payouts state could turn cancelled even if payment was successful 2024-02-08 16:32:41 +09:00
0bf22ddf29 Do not require user approval by default ()
As discussed on Mattermost.
2024-02-06 17:04:18 +09:00
1c4dc382a8 Merge pull request from pavlenex/release-cycles-doc
Create RELEASE-CYCLES.md
2024-02-05 19:42:15 +05:00
71c5566f2b Dashboard: Tooltip for balance on a particular day ()
Closes .
2024-02-02 11:29:35 +01:00
6621859567 remove decimals for Colombian (COP) and Argentina's Peso (ARS) ()
* remove decimals for Colombian (COP) and Argentina's Peso (ARS)

* remove js currency hardcoding

* Fixes removal of columbia and argentina's peso

* Refactor

---------

Co-authored-by: nicolas.dorier <nicolas.dorier@gmail.com>
2024-02-02 17:16:13 +09:00
6437967e60 Fix: Closing Balance in Dashboard was showing incorrect value () 2024-02-01 15:13:05 +09:00
c5a926c50c Fix Kraken rate for LTC 2024-02-01 14:45:59 +09:00
85ab691b68 bump version 2024-02-01 14:17:14 +09:00
4d3e0ab599 Changelog 2024-02-01 10:13:18 +09:00
02663a149e Fix Kraken API 2024-02-01 10:09:32 +09:00
a8fdc4798d Remove randomize RBF from wallet UI advanced settings ()
* Remove randomize RBF from wallet UI advanced settings

* remove support RBF and allow bump fee from wallet send model

* update psbt RBF
2024-01-31 21:04:19 +09:00
6290b0f3bf Admins can approve registered users ()
* Users list: Cleanups

* Policies: Flip registration settings

* Policies: Add RequireUserApproval setting

* Add approval to user

* Require approval on login and for API key

* API handling

* AccountController cleanups

* Test fix

* Apply suggestions from code review

Co-authored-by: Nicolas Dorier <nicolas.dorier@gmail.com>

* Add missing imports

* Communicate login requirements to user on account creation

* Add login requirements to basic auth handler

* Cleanups and test fix

* Encapsulate approval logic in user service and log approval changes

* Send follow up "Account approved" email

Closes .

* Add notification for admins

* Fix creating a user via the admin view

* Update list: Unify flags into status column, add approve action

* Adjust "Resend email" wording

* Incorporate feedback from code review

* Remove duplicate test server policy reset

---------

Co-authored-by: Nicolas Dorier <nicolas.dorier@gmail.com>
2024-01-31 14:45:54 +09:00
411e0334d0 Bitnob rate provider ()
* Bitnob rate provider

* Add Bitnob as recommended exchange for NGN
2024-01-30 10:18:42 +09:00
b174977bc7 Store Email Settings: Improve configuration ()
* Store Email Settings: Improve configuration

This works with the existing settings and provides better guidance about the different store email cases. Closes .

* Split email and notification settings
2024-01-26 10:28:50 +01:00
2111b67e2c Update changelog 2024-01-25 21:03:27 +09:00
b96cfcd14d Apps: Allow authenticated, non-owner users permissioned access ()
Fixes . Before this, the app lookup was constrained by the user having at least `CanModifyStoreSettings` permissions. This changes it to require the user being associated with a store, leaving the fine-grained authorization checks up to the individual actions.
2024-01-25 21:00:33 +09:00
086f713752 Wizard UI: Constrain navigation width ()
This way the back and close buttons stay within the regular container size on and don't stick to the left and right end on wide screens.

Closes .
2024-01-25 16:38:05 +09:00
fd67e09cf0 In Wallet Send, label were not applied to transactions () 2024-01-25 16:37:49 +09:00
6f4ca47532 Add documentation for wallet export on sparrow 2024-01-25 16:37:15 +09:00
f97f23c8a5 Do not dispose connections created by EF 2024-01-25 10:45:02 +09:00
b62985faf4 Update changelog 2024-01-24 22:55:23 +09:00
09c761aa31 Fix: Sometimes importing a wallet file from Electrum would fail 2024-01-24 22:53:40 +09:00
8089a938f3 Guest role: Fix redirect after store creation ()
This ensures that guests land on the invoices list, which tehy are allowed to see — rather than the dashboard, which they don't have permissions for.

Fixes .
2024-01-24 11:34:16 +01:00
35b3fef7c5 Fix wallet import ()
* Fix wallet import

* Improve error message for import of wallet file
2024-01-24 17:49:15 +09:00
f31aa43c6a Wallet file parsing: Add Wasabi test case and re-add Electrum distinction ()
* Extend tests, add Wasabi file

* Re-add Electrum distinction

* Specter: Fix indentation

* Cleanups
2024-01-24 09:28:22 +09:00
b03f8db06b Refactor wallet file parsing (Fix: ) () 2024-01-23 21:33:45 +09:00
27e70a169e Do not show warning about browser compatibility to vault on confirm address 2024-01-23 21:30:29 +09:00
6a1d17dda2 Remove ESC as a supporter ()
Closes .
2024-01-22 09:00:10 +09:00
95bf60c252 Create RELEASE-CYCLES.md
This PR adds documentation around release cycles in BTCPay and tends to outline processes and ensures there's documented structure on roles and responsibilities. Feedback welcome.
2024-01-20 13:44:39 +01:00
31bc6dd48c More tests on interpolation 2024-01-20 12:21:58 +09:00
f9a43b537f Remove disclaimer for lightning being in experimentation 2023-12-24 11:12:51 +09:00
1165 changed files with 41853 additions and 74972 deletions
.gitignore
.vscode
BTCPayServer.Abstractions
BTCPayServer.Client
BTCPayServer.Client.csprojBTCPayServerClient.APIKeys.csBTCPayServerClient.Apps.csBTCPayServerClient.Authorization.csBTCPayServerClient.CustodianAccounts.csBTCPayServerClient.Custodians.csBTCPayServerClient.Files.csBTCPayServerClient.Health.csBTCPayServerClient.Invoices.csBTCPayServerClient.LNURLPayPaymentMethods.csBTCPayServerClient.Lightning.Internal.csBTCPayServerClient.Lightning.Store.csBTCPayServerClient.LightningAddresses.csBTCPayServerClient.LightningNetworkPaymentMethods.csBTCPayServerClient.Misc.csBTCPayServerClient.Notifications.csBTCPayServerClient.OnChainPaymentMethods.csBTCPayServerClient.OnChainWallet.Objects.csBTCPayServerClient.OnChainWallet.csBTCPayServerClient.PaymentRequests.csBTCPayServerClient.PayoutProcessors.csBTCPayServerClient.PullPayments.csBTCPayServerClient.ServerInfo.csBTCPayServerClient.StoreEmail.csBTCPayServerClient.StorePaymentMethods.csBTCPayServerClient.StorePayoutProcessors.csBTCPayServerClient.StoreRatesConfiguration.csBTCPayServerClient.StoreUsers.csBTCPayServerClient.Stores.csBTCPayServerClient.Users.csBTCPayServerClient.Webhooks.csBTCPayServerClient.csGreenFieldAPIException.csGreenFieldValidationException.cs
JsonConverters
Models
AppBaseData.csAppItemStats.csAppSalesStats.csApplicationUserData.csApproveUserRequest.csCreateAppRequest.csCreateApplicationUserRequest.csCreateCustodianAccountRequest.csCreateOnChainTransactionRequest.csCreatePayoutRequest.csCreatePullPaymentRequest.csCrowdfundAppData.csCustodianAccountBaseData.csCustodianAccountData.csCustodianAccountResponse.csCustodianData.csDepositAddressData.csEmailSettingsData.csFileData.csGenerateOnChainWalletRequest.csGenericPaymentMethodData.csGreenfieldAPIError.csGreenfieldValidationError.csInvoiceData.csInvoiceExceptionStatus.csInvoicePaymentMethodDataModel.csLNURLPayPaymentMethodBaseData.csLNURLPayPaymentMethodData.csLightningAddressData.csLightningAutomatedPayoutSettings.csLightningNetworkPaymentMethodBaseData.csLightningNetworkPaymentMethodData.csMarketTradeResponseData.csNotificationData.csNotificationSettingsData.csOnChainAutomatedPayoutSettings.csOnChainPaymentMethodBaseData.csOnChainPaymentMethodData.csOnChainPaymentMethodDataWithSensitiveData.csOnChainWalletUTXOData.csPaymentMethodCriteriaData.csPaymentRequestBaseData.csPayoutData.csPayoutProcessorData.csPointOfSaleAppData.csPullPaymentBaseData.csRefundInvoiceRequest.csRegisterBoltcardRequest.csServerInfoData.csStoreBaseData.csTradeQuoteResponseData.csTradeRequestData.csUpdateApplicationUserRequest.csUpdateLightningNetworkPaymentMethodRequest.csUpdateNotificationSettingsRequest.csUpdateOnChainPaymentMethodRequest.csWebhookInvoiceEvent.csWithdrawRequestData.csWithdrawalBaseResponseData.csWithdrawalResponseData.csWithdrawalSimulationResponseData.cs
Permissions.cs
BTCPayServer.Common
BTCPayServer.Data
ApplicationDbContext.csApplicationDbContextFactory.csBTCPayServer.Data.csproj
DBScripts
DBScriptsMigration.cs
Data
Migrations
20170913143004_Init.cs20170926073744_Settings.cs20171006013443_AddressMapping.cs20171010082424_Tokens.cs20171012020112_PendingInvoices.cs20171024163354_RenewUsedAddresses.cs20171105235734_PaymentAccounted.cs20171221054550_AltcoinSupport.cs20180106095215_DerivationStrategies.cs20180109021122_defaultcrypto.cs20180114123253_events.cs20180402095640_appdata.cs20180429083930_legacyapikey.cs20180719095626_CanDeleteStores.cs20190121133309_AddPaymentRequests.cs20190219032533_AppsTagging.cs20190225091644_AddOpenIddict.cs20190324141717_AddFiles.cs20190425081749_AddU2fDevices.cs20190701082105_sort_paymentrequests.cs20190802142637_WalletData.cs20200110064617_OpenIddictUpdate.cs20200119130108_ExtendApiKeys.cs20200224134444_Remove_OpenIddict.cs20200225133433_AddApiKeyLabel.cs20200402065615_AddApiKeyBlob.cs20200507092343_AddArchivedToInvoice.cs20200625064111_refundnotificationpullpayments.cs20200901161733_AddInvoiceEventLogSeverity.cs20201002145033_AddCreateDateToUser.cs20201007090617_u2fDeviceCascade.cs20201208054211_invoicesorderindex.cs20210314092253_Fido2Credentials.cs20220311135252_AddPayoutProcessors.cs20220414132313_AddLightningAddress.cs20220610090843_AddSettingsToStore.cs20220929132704_label.cs20221128062447_jsonb.cs20230123062447_migrateoldratesource.cs20230125085242_AddForms.cs20230130040047_blob2.cs20230130062447_jsonb2.cs20230315062447_fixmaxlength.cs20230504125505_StoreRoles.cs20230529135505_WebhookDeliveriesCleanup.cs20231121031609_removecurrentrefund.cs20231219031609_appssettingstojson.cs20231219031609_translationsmigration.cs20240104155620_AddApprovalToApplicationUser.cs20240220000000_FixWalletObjectsWithEmptyWalletId.cs20240229000000_PayoutAndPullPaymentToJsonBlob.cs20240229092905_AddManagerAndEmployeeToStoreRoles.cs20240304003640_addinvoicecolumns.cs20240317024757_payments_refactor.cs20240325095923_RemoveCustodian.cs20240405004015_cleanup_invoice_events.cs20240501015052_noperiod.cs20240508015052_fileid.cs20240826065950_removeinvoicecols.cs20240827034505_migratepayouts.cs20240904092905_UpdateStoreOwnerRole.cs20240913034505_refactorpendinginvoicespayments.cs20240919085726_refactorinvoiceaddress.cs20240923065254_refactorpayments.cs20240924065254_monitoredinvoices.csApplicationDbContextModelSnapshot.cs
MigrationsExtensions.cs
BTCPayServer.PluginPacker
BTCPayServer.Rating
BTCPayServer.Tests
BTCPayServer
BTCPayServer.csproj
Blazor
Components
Configuration
Controllers
BitpayInvoiceController.csBitpayRateController.cs
GreenField
UIAccountController.csUIAppsController.csUIBoltcardController.csUICustodianAccountsController.csUIHomeController.csUIInvoiceController.Testing.csUIInvoiceController.UI.csUIInvoiceController.csUILNURLAuthController.csUILNURLController.csUIManageController.2FA.csUIManageController.APIKeys.csUIManageController.LoginCodes.csUIManageController.Notifications.csUIManageController.csUINotificationsController.csUIPaymentRequestController.csUIPublicController.csUIPublicLightningNodeInfoController.csUIPullPaymentController.Boltcard.csUIPullPaymentController.csUIReportsController.CheatMode.csUIReportsController.csUIServerController.Plugins.csUIServerController.Roles.csUIServerController.Storage.csUIServerController.Translations.csUIServerController.Users.csUIServerController.csUIStorePullPaymentsController.PullPayments.csUIStoresController.Dashboard.csUIStoresController.Email.csUIStoresController.Integrations.csUIStoresController.LightningLike.csUIStoresController.Onchain.csUIStoresController.Rates.csUIStoresController.Roles.csUIStoresController.Settings.csUIStoresController.Tokens.csUIStoresController.Users.csUIStoresController.csUIUserStoresController.csUIVaultController.csUIWalletsController.PSBT.csUIWalletsController.cs
CurrencyValue.cs
Data
DefaultRules.csDerivationSchemeParser.csDerivationSchemeSettings.cs
Events
Extensions.cs
Extensions
Fido2
Filters
Forms
HandlersDictionary.cs
HostedServices
Hosting
JsonConverters
ModelBinders
Models
PaymentRequest
Payments
PayoutProcessors
Payouts
Plugins
Program.cs
Properties
ResourceTracker.csSearchString.cs
Security
Services
Altcoins
Apps
BTCPayNetworkJsonSerializerSettings.csBTCPayServerEnvironment.csCheater.cs
Custodian
DisplayFormatter.cs
Fees
InvoiceActivator.cs
Invoices
LanguageService.csLocalizerFactory.csLocalizerService.cs
Mails
MigrationSettings.csNBXSyncSummaryProvider.cs
Notifications
PoliciesSettings.csPrettyNameProvider.csReportService.cs
Reporting
ServerSettings.cs
Stores
ThemesSettings.csTransactionLinkProviders.csTranslations.Default.csTranslations.csUriResolver.csUserService.csWalletFileParsers.cs
WalletFileParsing
WalletRepository.cs
Wallets
Storage/Services
StorePolicies.cs
TagHelpers
UnresolvedUri.csUserManagerExtensions.csVaultClient.cs
Views
Shared
UIAccount
UIApps
UICustodianAccounts
UIError
UIFido2
UIForms
UIHome
UIInvoice
UILNURL
UILNURLAuth
UILightning
UILightningAutomatedPayoutProcessors
UILightningLikePayout
UIManage
UIMoneroLikeStore
UINotifications
UIOnChainAutomatedPayoutProcessors
UIPaymentRequest
UIPayoutProcessors
UIPublic
UIPublicLightningNodeInfo
UIPullPayment
UIReports
UIServer
UIShopify
UIStorePullPayments
UIStores
UIUserStores
UIWallets
UIZcashLikeStore
WalletId.cs_ViewImports.cshtml
wwwroot
checkout
crowdfund
img
js
locales
main
payment-request
pos
shopify
swagger/v1
vendor
Build
Changelog.mdDockerfileREADME.mdRELEASE-CYCLES.mdbtcpayserver.sln
docs

1
.gitignore vendored

@ -300,3 +300,4 @@ Plugins/packed
BTCPayServer/wwwroot/swagger/v1/openapi.json
BTCPayServer/appsettings.dev.json
BTCPayServer.Tests/monero_wallet
/BTCPayServer.Tests/NewBlocks.bat

2
.vscode/launch.json vendored

@ -10,7 +10,7 @@
"request": "launch",
"preLaunchTask": "build",
// If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/BTCPayServer/bin/Debug/net6.0/BTCPayServer.dll",
"program": "${workspaceFolder}/BTCPayServer/bin/Debug/net8.0/BTCPayServer.dll",
"args": [],
"cwd": "${workspaceFolder}/BTCPayServer",
"stopAtEntry": false,

@ -31,11 +31,9 @@
<None Include="icon.png" Pack="true" PackagePath="\" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="HtmlSanitizer" Version="8.0.723" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.1" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.0" />
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="8.0.0-beta.2" />
<PackageReference Include="HtmlSanitizer" Version="8.0.838" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.6" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.4" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\BTCPayServer.Client\BTCPayServer.Client.csproj" />

@ -9,6 +9,7 @@ namespace BTCPayServer.Configuration
public string TempStorageDir { get; set; }
public string StorageDir { get; set; }
public string TempDir { get; set; }
public string LangsDir { get; set; }
public string ToDatadirFullPath(string path)
{

@ -1,10 +1,11 @@
using System;
using System.Data.Common;
using BTCPayServer.Abstractions.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.Extensions.Options;
using Npgsql;
using Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure;
using Npgsql.EntityFrameworkCore.PostgreSQL.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Migrations.Operations;
@ -13,15 +14,16 @@ namespace BTCPayServer.Abstractions.Contracts
public abstract class BaseDbContextFactory<T> where T : DbContext
{
private readonly IOptions<DatabaseOptions> _options;
private readonly string _schemaPrefix;
private readonly string _migrationTableName;
public BaseDbContextFactory(IOptions<DatabaseOptions> options, string schemaPrefix)
public BaseDbContextFactory(IOptions<DatabaseOptions> options, string migrationTableName)
{
_options = options;
_schemaPrefix = schemaPrefix;
_migrationTableName = migrationTableName;
}
public abstract T CreateContext();
public T CreateContext() => CreateContext(null);
public abstract T CreateContext(Action<NpgsqlDbContextOptionsBuilder> npgsqlOptionsAction = null);
class CustomNpgsqlMigrationsSqlGenerator : NpgsqlMigrationsSqlGenerator
{
#pragma warning disable EF1001 // Internal EF Core API usage.
@ -66,47 +68,27 @@ namespace BTCPayServer.Abstractions.Contracts
}
}
public void ConfigureBuilder(DbContextOptionsBuilder builder)
public void ConfigureBuilder(DbContextOptionsBuilder builder) => ConfigureBuilder(builder, null);
public void ConfigureBuilder(DbContextOptionsBuilder builder, Action<NpgsqlDbContextOptionsBuilder> npgsqlOptionsAction = null)
{
switch (_options.Value.DatabaseType)
builder
.UseNpgsql(_options.Value.ConnectionString, o =>
{
case DatabaseType.Sqlite:
builder.UseSqlite(_options.Value.ConnectionString, o =>
{
if (!string.IsNullOrEmpty(_schemaPrefix))
{
o.MigrationsHistoryTable(_schemaPrefix);
}
});
break;
case DatabaseType.Postgres:
builder
.UseNpgsql(_options.Value.ConnectionString, o =>
{
o.EnableRetryOnFailure(10);
o.SetPostgresVersion(12, 0);
if (!string.IsNullOrEmpty(_schemaPrefix))
{
o.MigrationsHistoryTable(_schemaPrefix);
}
})
.ReplaceService<IMigrationsSqlGenerator, CustomNpgsqlMigrationsSqlGenerator>();
break;
case DatabaseType.MySQL:
builder.UseMySql(_options.Value.ConnectionString, ServerVersion.AutoDetect(_options.Value.ConnectionString), o =>
{
o.EnableRetryOnFailure(10);
if (!string.IsNullOrEmpty(_schemaPrefix))
{
o.MigrationsHistoryTable(_schemaPrefix);
}
});
break;
default:
throw new ArgumentOutOfRangeException();
}
o.EnableRetryOnFailure(10);
o.SetPostgresVersion(12, 0);
npgsqlOptionsAction?.Invoke(o);
var mainSearchPath = GetSearchPath(_options.Value.ConnectionString);
var schemaPrefix = string.IsNullOrEmpty(_migrationTableName) ? "__EFMigrationsHistory" : _migrationTableName;
o.MigrationsHistoryTable(schemaPrefix, mainSearchPath);
})
.ReplaceService<IMigrationsSqlGenerator, CustomNpgsqlMigrationsSqlGenerator>();
}
private string GetSearchPath(string connectionString)
{
var connectionStringBuilder = new NpgsqlConnectionStringBuilder(connectionString);
var searchPaths = connectionStringBuilder.SearchPath?.Split(',');
return searchPaths is not { Length: > 0 } ? null : searchPaths[0];
}
}
}

@ -2,6 +2,7 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using BTCPayServer.Abstractions.Models;
namespace BTCPayServer.Abstractions.Contracts;
@ -14,4 +15,5 @@ public interface IFileService
Task<string?> GetTemporaryFileUrl(Uri baseUri, string fileId, DateTimeOffset expiry,
bool isDownload);
Task RemoveFile(string fileId, string userId);
Task<UploadImageResultModel> UploadImage(IFormFile file, string userId, long maxFileSizeInBytes = 1_000_000);
}

@ -25,5 +25,6 @@ namespace BTCPayServer.Abstractions.Contracts
public string Body { get; set; }
public string ActionLink { get; set; }
public bool Seen { get; set; }
public string StoreId { get; set; }
}
}

@ -12,7 +12,7 @@ namespace BTCPayServer.Abstractions.Contracts
public interface ISyncStatus
{
public string CryptoCode { get; set; }
public string PaymentMethodId { get; set; }
public bool Available { get; }
}
}

@ -1,19 +0,0 @@
namespace BTCPayServer.Abstractions.Custodians.Client;
public class AssetQuoteResult
{
public string FromAsset { get; set; }
public string ToAsset { get; set; }
public decimal Bid { get; set; }
public decimal Ask { get; set; }
public AssetQuoteResult() { }
public AssetQuoteResult(string fromAsset, string toAsset, decimal bid, decimal ask)
{
FromAsset = fromAsset;
ToAsset = toAsset;
Bid = bid;
Ask = ask;
}
}

@ -1,12 +0,0 @@
namespace BTCPayServer.Abstractions.Custodians;
public class AssetBalancesUnavailableException : CustodianApiException
{
public AssetBalancesUnavailableException(System.Exception e) : base(500, "asset-balances-unavailable", $"Cannot fetch the asset balances: {e.Message}", e)
{
}
public AssetBalancesUnavailableException(string errorMsg) : base(500, "asset-balances-unavailable", $"Cannot fetch the asset balances: {errorMsg}")
{
}
}

@ -1,13 +0,0 @@
using BTCPayServer.Client.Models;
namespace BTCPayServer.Abstractions.Custodians;
public class AssetQuoteUnavailableException : CustodianApiException
{
public AssetPairData AssetPair { get; }
public AssetQuoteUnavailableException(AssetPairData assetPair) : base(400, "asset-price-unavailable", "Cannot find a quote for pair " + assetPair)
{
this.AssetPair = assetPair;
}
}

@ -1,13 +0,0 @@
using System;
namespace BTCPayServer.Abstractions.Custodians;
public class BadConfigException : CustodianApiException
{
public string[] BadConfigKeys { get; set; }
public BadConfigException(string[] badConfigKeys) : base(500, "bad-custodian-account-config", "Wrong config values: " + String.Join(", ", badConfigKeys))
{
this.BadConfigKeys = badConfigKeys;
}
}

@ -1,13 +0,0 @@
namespace BTCPayServer.Abstractions.Custodians;
public class CannotWithdrawException : CustodianApiException
{
public CannotWithdrawException(ICustodian custodian, string paymentMethod, string message) : base(403, "cannot-withdraw", message)
{
}
public CannotWithdrawException(ICustodian custodian, string paymentMethod, string targetAddress, CustodianApiException originalException) : base(403, "cannot-withdraw", $"{custodian.Name} cannot withdraw {paymentMethod} to '{targetAddress}': {originalException.Message}")
{
}
}

@ -1,18 +0,0 @@
using System;
namespace BTCPayServer.Abstractions.Custodians;
public class CustodianApiException : Exception
{
public int HttpStatus { get; }
public string Code { get; }
public CustodianApiException(int httpStatus, string code, string message, System.Exception ex) : base(message, ex)
{
HttpStatus = httpStatus;
Code = code;
}
public CustodianApiException(int httpStatus, string code, string message) : this(httpStatus, code, message, null)
{
}
}

@ -1,8 +0,0 @@
namespace BTCPayServer.Abstractions.Custodians;
public class CustodianFeatureNotImplementedException : CustodianApiException
{
public CustodianFeatureNotImplementedException(string message) : base(400, "not-implemented", message)
{
}
}

@ -1,8 +0,0 @@
namespace BTCPayServer.Abstractions.Custodians;
public class DepositsUnavailableException : CustodianApiException
{
public DepositsUnavailableException(string message) : base(404, "deposits-unavailable", message)
{
}
}

@ -1,8 +0,0 @@
namespace BTCPayServer.Abstractions.Custodians;
public class InsufficientFundsException : CustodianApiException
{
public InsufficientFundsException(string message) : base(400, "insufficient-funds", message)
{
}
}

@ -1,9 +0,0 @@
namespace BTCPayServer.Abstractions.Custodians;
public class InvalidWithdrawalTargetException : CustodianApiException
{
public InvalidWithdrawalTargetException(ICustodian custodian, string paymentMethod, string targetAddress, CustodianApiException originalException) : base(403, "invalid-withdrawal-target", $"{custodian.Name} cannot withdraw {paymentMethod} to '{targetAddress}': {originalException.Message}")
{
}
}

@ -1,9 +0,0 @@
namespace BTCPayServer.Abstractions.Custodians;
public class PermissionDeniedCustodianApiException : CustodianApiException
{
public PermissionDeniedCustodianApiException(ICustodian custodian) : base(403, "custodian-api-permission-denied", $"{custodian.Name}'s API reported that you don't have permission.")
{
}
}

@ -1,11 +0,0 @@
namespace BTCPayServer.Abstractions.Custodians;
public class TradeNotFoundException : CustodianApiException
{
private string tradeId { get; }
public TradeNotFoundException(string tradeId) : base(404, "trade-not-found", "Could not find trade ID " + tradeId)
{
this.tradeId = tradeId;
}
}

@ -1,11 +0,0 @@
namespace BTCPayServer.Abstractions.Custodians;
public class WithdrawalNotFoundException : CustodianApiException
{
private string WithdrawalId { get; }
public WithdrawalNotFoundException(string withdrawalId) : base(404, "withdrawal-not-found", $"Could not find withdrawal ID {withdrawalId}.")
{
WithdrawalId = withdrawalId;
}
}

@ -1,9 +0,0 @@
namespace BTCPayServer.Abstractions.Custodians;
public class WrongTradingPairException : CustodianApiException
{
public const int HttpCode = 404;
public WrongTradingPairException(string fromAsset, string toAsset) : base(HttpCode, "wrong-trading-pair", $"Cannot find a trading pair for converting {fromAsset} into {toAsset}.")
{
}
}

@ -1,29 +0,0 @@
using System.Collections.Generic;
using BTCPayServer.Client.Models;
namespace BTCPayServer.Abstractions.Custodians.Client;
/**
* The result of a market trade. Used as a return type for custodians implementing ICanTrade
*/
public class MarketTradeResult
{
public string FromAsset { get; }
public string ToAsset { get; }
/**
* The ledger entries that show the balances that were affected by the trade.
*/
public List<LedgerEntryData> LedgerEntries { get; }
/**
* The unique ID of the trade that was executed.
*/
public string TradeId { get; }
public MarketTradeResult(string fromAsset, string toAsset, List<LedgerEntryData> ledgerEntries, string tradeId)
{
this.FromAsset = fromAsset;
this.ToAsset = toAsset;
this.LedgerEntries = ledgerEntries;
this.TradeId = tradeId;
}
}

@ -1,28 +0,0 @@
using System.Collections.Generic;
using BTCPayServer.Client.Models;
using BTCPayServer.JsonConverters;
namespace BTCPayServer.Abstractions.Custodians.Client;
public class SimulateWithdrawalResult
{
public string PaymentMethod { get; }
public string Asset { get; }
public decimal MinQty { get; }
public decimal MaxQty { get; }
public List<LedgerEntryData> LedgerEntries { get; }
// Fee can be NULL if unknown.
public decimal? Fee { get; }
public SimulateWithdrawalResult(string paymentMethod, string asset, List<LedgerEntryData> ledgerEntries,
decimal minQty, decimal maxQty)
{
PaymentMethod = paymentMethod;
Asset = asset;
LedgerEntries = ledgerEntries;
MinQty = minQty;
MaxQty = maxQty;
}
}

@ -1,29 +0,0 @@
using System;
using System.Collections.Generic;
using BTCPayServer.Client.Models;
namespace BTCPayServer.Abstractions.Custodians.Client;
public class WithdrawResult
{
public string PaymentMethod { get; }
public string Asset { get; set; }
public List<LedgerEntryData> LedgerEntries { get; }
public string WithdrawalId { get; }
public WithdrawalResponseData.WithdrawalStatus Status { get; }
public DateTimeOffset CreatedTime { get; }
public string TargetAddress { get; }
public string TransactionId { get; }
public WithdrawResult(string paymentMethod, string asset, List<LedgerEntryData> ledgerEntries, string withdrawalId, WithdrawalResponseData.WithdrawalStatus status, DateTimeOffset createdTime, string targetAddress, string transactionId)
{
PaymentMethod = paymentMethod;
Asset = asset;
LedgerEntries = ledgerEntries;
WithdrawalId = withdrawalId;
CreatedTime = createdTime;
Status = status;
TargetAddress = targetAddress;
TransactionId = transactionId;
}
}

@ -1,17 +0,0 @@
using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Client.Models;
using Newtonsoft.Json.Linq;
namespace BTCPayServer.Abstractions.Custodians;
public interface ICanDeposit
{
/**
* Get the address where we can deposit for the chosen payment method (crypto code + network).
* The result can be a string in different formats like a bitcoin address or even a LN invoice.
*/
public Task<DepositAddressData> GetDepositAddressAsync(string paymentMethod, JObject config, CancellationToken cancellationToken);
public string[] GetDepositablePaymentMethods();
}

@ -1,31 +0,0 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Abstractions.Custodians.Client;
using BTCPayServer.Client.Models;
using Newtonsoft.Json.Linq;
namespace BTCPayServer.Abstractions.Custodians;
public interface ICanTrade
{
/**
* A list of tradable asset pairs, or NULL if the custodian cannot trade/convert assets. if thr asset pair contains fiat, fiat is always put last. If both assets are a cyrptocode or both are fiat, the pair is written alphabetically. Always in uppercase. Example: ["BTC/EUR","BTC/USD", "EUR/USD", "BTC/ETH",...]
*/
public List<AssetPairData> GetTradableAssetPairs();
/**
* Execute a market order right now.
*/
public Task<MarketTradeResult> TradeMarketAsync(string fromAsset, string toAsset, decimal qty, JObject config, CancellationToken cancellationToken);
/**
* Get the details about a previous market trade.
*/
public Task<MarketTradeResult> GetTradeInfoAsync(string tradeId, JObject config, CancellationToken cancellationToken);
public Task<AssetQuoteResult> GetQuoteForAssetAsync(string fromAsset, string toAsset, JObject config, CancellationToken cancellationToken);
}

@ -1,20 +0,0 @@
using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Abstractions.Custodians.Client;
using Newtonsoft.Json.Linq;
namespace BTCPayServer.Abstractions.Custodians;
/// <summary>
/// Interface for custodians that can move funds to the store wallet.
/// </summary>
public interface ICanWithdraw
{
public Task<WithdrawResult> WithdrawToStoreWalletAsync(string paymentMethod, decimal amount, JObject config, CancellationToken cancellationToken);
public Task<SimulateWithdrawalResult> SimulateWithdrawalAsync(string paymentMethod, decimal qty, JObject config, CancellationToken cancellationToken);
public Task<WithdrawResult> GetWithdrawalInfoAsync(string paymentMethod, string withdrawalId, JObject config, CancellationToken cancellationToken);
public string[] GetWithdrawablePaymentMethods();
}

@ -1,26 +0,0 @@
#nullable enable
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Client.Models;
using Newtonsoft.Json.Linq;
namespace BTCPayServer.Abstractions.Custodians;
public interface ICustodian
{
/**
* Get the unique code that identifies this custodian.
*/
string Code { get; }
string Name { get; }
/**
* Get a list of assets and their qty in custody.
*/
Task<Dictionary<string, decimal>> GetAssetBalancesAsync(JObject config, CancellationToken cancellationToken);
public Task<Form.Form> GetConfigForm(JObject config, CancellationToken cancellationToken = default);
}

@ -1,14 +0,0 @@
#nullable enable
using System.Collections.Generic;
using System.Linq;
using BTCPayServer.Abstractions.Custodians;
namespace BTCPayServer.Abstractions.Extensions;
public static class CustodianExtensions
{
public static ICustodian? GetCustodianByCode(this IEnumerable<ICustodian> custodians, string code)
{
return custodians.FirstOrDefault(custodian => custodian.Code == code);
}
}

@ -36,6 +36,17 @@ public static class HttpRequestExtensions
request.Path.ToUriComponent());
}
public static string GetCurrentUrlWithQueryString(this HttpRequest request)
{
return string.Concat(
request.Scheme,
"://",
request.Host.ToUriComponent(),
request.PathBase.ToUriComponent(),
request.Path.ToUriComponent(),
request.QueryString.ToUriComponent());
}
public static string GetCurrentPath(this HttpRequest request)
{
return string.Concat(

@ -8,6 +8,14 @@ namespace BTCPayServer.Abstractions.Extensions;
public static class SetStatusMessageModelExtensions
{
public static void SetStatusSuccess(this ITempDataDictionary tempData, string statusMessage)
{
tempData.SetStatusMessageModel(new StatusMessageModel
{
Severity = StatusMessageModel.StatusSeverity.Success,
Message = statusMessage
});
}
public static void SetStatusMessageModel(this ITempDataDictionary tempData, StatusMessageModel statusMessage)
{
if (statusMessage == null)
@ -26,19 +34,14 @@ public static class SetStatusMessageModelExtensions
tempData.TryGetValue("StatusMessageModel", out var model);
if (successMessage != null || errorMessage != null)
{
var parsedModel = new StatusMessageModel();
parsedModel.Message = (string)successMessage ?? (string)errorMessage;
if (successMessage != null)
var parsedModel = new StatusMessageModel
{
parsedModel.Severity = StatusMessageModel.StatusSeverity.Success;
}
else
{
parsedModel.Severity = StatusMessageModel.StatusSeverity.Error;
}
Message = (string)successMessage ?? (string)errorMessage,
Severity = successMessage != null ? StatusMessageModel.StatusSeverity.Success : StatusMessageModel.StatusSeverity.Error
};
return parsedModel;
}
else if (model != null && model is string str)
if (model is string str)
{
return JObject.Parse(str).ToObject<StatusMessageModel>();
}

@ -12,7 +12,7 @@ namespace BTCPayServer.Abstractions.Extensions
private const string ACTIVE_CATEGORY_KEY = "ActiveCategory";
private const string ACTIVE_PAGE_KEY = "ActivePage";
private const string ACTIVE_ID_KEY = "ActiveId";
private const string ActivePageClass = "active";
private const string ACTIVE_CLASS = "active";
public enum DateDisplayFormat
{
@ -55,50 +55,101 @@ namespace BTCPayServer.Abstractions.Extensions
viewData[ACTIVE_CATEGORY_KEY] = activeCategory;
}
public static string IsActiveCategory<T>(this ViewDataDictionary viewData, T category, object id = null)
public static bool IsCategoryActive(this ViewDataDictionary viewData, string category, object id = null)
{
return IsActiveCategory(viewData, category.ToString(), id);
}
public static string IsActiveCategory(this ViewDataDictionary viewData, string category, object id = null)
{
if (!viewData.ContainsKey(ACTIVE_CATEGORY_KEY))
{
return null;
}
if (!viewData.ContainsKey(ACTIVE_CATEGORY_KEY)) return false;
var activeId = viewData[ACTIVE_ID_KEY];
var activeCategory = viewData[ACTIVE_CATEGORY_KEY]?.ToString();
var categoryMatch = category.Equals(activeCategory, StringComparison.InvariantCultureIgnoreCase);
var idMatch = id == null || activeId == null || id.Equals(activeId);
return categoryMatch && idMatch ? ActivePageClass : null;
return categoryMatch && idMatch;
}
public static string IsActivePage<T>(this ViewDataDictionary viewData, T page, object id = null)
where T : IConvertible
public static bool IsCategoryActive<T>(this ViewDataDictionary viewData, T category, object id = null)
{
return IsActivePage(viewData, page.ToString(), page.GetType().ToString(), id);
return IsCategoryActive(viewData, category.ToString(), id);
}
public static string IsActivePage<T>(this ViewDataDictionary viewData, IEnumerable<T> pages, object id = null)
where T : IConvertible
public static bool IsPageActive(this ViewDataDictionary viewData, string page, string category, object id = null)
{
return pages.Any(page => IsActivePage(viewData, page.ToString(), page.GetType().ToString(), id) == ActivePageClass)
? ActivePageClass
: null;
}
public static string IsActivePage(this ViewDataDictionary viewData, string page, string category, object id = null)
{
if (!viewData.ContainsKey(ACTIVE_PAGE_KEY))
{
return null;
}
if (!viewData.ContainsKey(ACTIVE_PAGE_KEY)) return false;
var activeId = viewData[ACTIVE_ID_KEY];
var activePage = viewData[ACTIVE_PAGE_KEY]?.ToString();
var activeCategory = viewData[ACTIVE_CATEGORY_KEY]?.ToString();
var categoryAndPageMatch = (category == null || activeCategory.Equals(category, StringComparison.InvariantCultureIgnoreCase)) && page.Equals(activePage, StringComparison.InvariantCultureIgnoreCase);
var categoryAndPageMatch = page.Equals(activePage, StringComparison.InvariantCultureIgnoreCase) &&
(category == null || activeCategory != null && activeCategory.Equals(category, StringComparison.InvariantCultureIgnoreCase));
var idMatch = id == null || activeId == null || id.Equals(activeId);
return categoryAndPageMatch && idMatch ? ActivePageClass : null;
return categoryAndPageMatch && idMatch;
}
public static bool IsPageActive<T>(this ViewDataDictionary viewData, IEnumerable<T> pages, object id = null)
where T : IConvertible
{
return pages.Any(page => ActivePageClass(viewData, page.ToString(), page.GetType().ToString(), id) == ACTIVE_CLASS);
}
public static string ActiveCategoryClass<T>(this ViewDataDictionary viewData, T category, object id = null)
{
return ActiveCategoryClass(viewData, category.ToString(), id);
}
public static string ActiveCategoryClass(this ViewDataDictionary viewData, string category, object id = null)
{
return IsCategoryActive(viewData, category, id) ? ACTIVE_CLASS : null;
}
public static string ActivePageClass<T>(this ViewDataDictionary viewData, T page, object id = null)
where T : IConvertible
{
return ActivePageClass(viewData, page.ToString(), page.GetType().ToString(), id);
}
public static string ActivePageClass(this ViewDataDictionary viewData, string page, string category, object id = null)
{
return IsPageActive(viewData, page, category, id) ? ACTIVE_CLASS : null;
}
public static string ActivePageClass<T>(this ViewDataDictionary viewData, IEnumerable<T> pages, object id = null) where T : IConvertible
{
return IsPageActive(viewData, pages, id) ? ACTIVE_CLASS : null;
}
[Obsolete("Use ActiveCategoryClass instead")]
public static string IsActiveCategory<T>(this ViewDataDictionary viewData, T category, object id = null)
{
return ActiveCategoryClass(viewData, category, id);
}
[Obsolete("Use ActiveCategoryClass instead")]
public static string IsActiveCategory(this ViewDataDictionary viewData, string category, object id = null)
{
return ActiveCategoryClass(viewData, category, id);
}
[Obsolete("Use ActivePageClass instead")]
public static string IsActivePage<T>(this ViewDataDictionary viewData, T page, object id = null) where T : IConvertible
{
return ActivePageClass(viewData, page, id);
}
[Obsolete("Use ActivePageClass instead")]
public static string IsActivePage<T>(this ViewDataDictionary viewData, IEnumerable<T> pages, object id = null) where T : IConvertible
{
return ActivePageClass(viewData, pages, id);
}
[Obsolete("Use ActivePageClass instead")]
public static string IsActivePage(this ViewDataDictionary viewData, string page, string category, object id = null)
{
return ActivePageClass(viewData, page, category, id);
}
public static HtmlString ToBrowserDate(this DateTimeOffset date, string netFormat, string jsDateFormat = "short", string jsTimeFormat = "short")
{
var dateTime = date.ToString("o", CultureInfo.InvariantCulture);
var displayDate = date.ToString(netFormat, CultureInfo.InvariantCulture);
var tooltip = dateTime.Replace("T", " ");
return new HtmlString($"<time datetime=\"{dateTime}\" data-date-style=\"{jsDateFormat}\" data-time-style=\"{jsTimeFormat}\" data-initial=\"localized\" data-bs-toggle=\"tooltip\" data-bs-title=\"{tooltip}\">{displayDate}</time>");
}
public static HtmlString ToBrowserDate(this DateTimeOffset date, DateDisplayFormat format = DateDisplayFormat.Localized)

@ -2,7 +2,6 @@ namespace BTCPayServer.Abstractions.Models
{
public class DatabaseOptions
{
public DatabaseType DatabaseType { get; set; }
public string ConnectionString { get; set; }
}
}

@ -1,9 +0,0 @@
namespace BTCPayServer.Abstractions.Models
{
public enum DatabaseType
{
Sqlite,
Postgres,
MySQL,
}
}

@ -14,14 +14,6 @@ namespace BTCPayServer.Abstractions.Models
public string SeverityCSS => ToString(Severity);
private void ParseNonJsonStatus(string s)
{
Message = s;
Severity = s.StartsWith("Error", StringComparison.InvariantCultureIgnoreCase)
? StatusSeverity.Error
: StatusSeverity.Success;
}
public static string ToString(StatusSeverity severity)
{
switch (severity)

@ -0,0 +1,16 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using BTCPayServer.Abstractions.Contracts;
namespace BTCPayServer.Abstractions.Models;
public class UploadImageResultModel
{
public bool Success { get; set; }
public string Response { get; set; } = string.Empty;
public IStoredFile? StoredFile { get; set; }
}

@ -1,9 +1,11 @@
using System;
using BTCPayServer.Abstractions.Contracts;
namespace BTCPayServer.Abstractions.Services
{
public class UIExtension : IUIExtension
{
[Obsolete("Use extension method BTCPayServer.Extensions.AddUIExtension(this IServiceCollection services, string location, string partialViewName) instead")]
public UIExtension(string partial, string location)
{
Partial = partial;

@ -0,0 +1,35 @@
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Razor.TagHelpers;
namespace BTCPayServer.Abstractions.TagHelpers;
[HtmlTargetElement("form", Attributes = "[permissioned]")]
public partial class PermissionedFormTagHelper(
IAuthorizationService authorizationService,
IHttpContextAccessor httpContextAccessor)
: TagHelper
{
public string Permissioned { get; set; }
public string PermissionResource { get; set; }
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
if (httpContextAccessor.HttpContext is null || string.IsNullOrEmpty(Permissioned))
return;
var res = await authorizationService.AuthorizeAsync(httpContextAccessor.HttpContext.User,
PermissionResource, Permissioned);
if (!res.Succeeded)
{
var content = await output.GetChildContentAsync();
var html = SubmitButtonRegex().Replace(content.GetContent(), "");
output.Content.SetHtmlContent($"<fieldset disabled>{html}</fieldset>");
}
}
[GeneratedRegex("<(button|input).*?type=\"submit\".*?>.*?</\\1>")]
private static partial Regex SubmitButtonRegex();
}

@ -12,11 +12,11 @@
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<RepositoryUrl>https://github.com/btcpayserver/btcpayserver</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<Configurations>Debug;Release;Altcoins-Debug;Altcoins-Release</Configurations>
<Configurations>Debug;Release</Configurations>
<Platforms>AnyCPU</Platforms>
</PropertyGroup>
<PropertyGroup>
<Version Condition=" '$(Version)' == '' ">1.7.3</Version>
<Version Condition=" '$(Version)' == '' ">2.0.0</Version>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<PublishRepositoryUrl>true</PublishRepositoryUrl>
@ -31,7 +31,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="BTCPayServer.Lightning.Common" Version="1.5.1" />
<PackageReference Include="NBitcoin" Version="7.0.34" />
<PackageReference Include="NBitcoin" Version="7.0.37" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>
<ItemGroup>

@ -1,58 +1,44 @@
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Client.Models;
namespace BTCPayServer.Client
namespace BTCPayServer.Client;
public partial class BTCPayServerClient
{
public partial class BTCPayServerClient
public virtual async Task<ApiKeyData> GetCurrentAPIKeyInfo(CancellationToken token = default)
{
public virtual async Task<ApiKeyData> GetCurrentAPIKeyInfo(CancellationToken token = default)
{
var response = await _httpClient.SendAsync(CreateHttpRequest("api/v1/api-keys/current"), token);
return await HandleResponse<ApiKeyData>(response);
}
return await SendHttpRequest<ApiKeyData>("api/v1/api-keys/current", null, HttpMethod.Get, token);
}
public virtual async Task<ApiKeyData> CreateAPIKey(CreateApiKeyRequest request, CancellationToken token = default)
{
if (request == null)
throw new ArgumentNullException(nameof(request));
var response = await _httpClient.SendAsync(CreateHttpRequest("api/v1/api-keys", bodyPayload: request, method: HttpMethod.Post), token);
return await HandleResponse<ApiKeyData>(response);
}
public virtual async Task<ApiKeyData> CreateAPIKey(CreateApiKeyRequest request, CancellationToken token = default)
{
if (request == null) throw new ArgumentNullException(nameof(request));
return await SendHttpRequest<ApiKeyData>("api/v1/api-keys", request, HttpMethod.Post, token);
}
public virtual async Task<ApiKeyData> CreateAPIKey(string userId, CreateApiKeyRequest request, CancellationToken token = default)
{
if (request == null)
throw new ArgumentNullException(nameof(request));
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/users/{userId}/api-keys",
bodyPayload: request, method: HttpMethod.Post), token);
return await HandleResponse<ApiKeyData>(response);
}
public virtual async Task<ApiKeyData> CreateAPIKey(string userId, CreateApiKeyRequest request, CancellationToken token = default)
{
if (request == null) throw new ArgumentNullException(nameof(request));
return await SendHttpRequest<ApiKeyData>($"api/v1/users/{userId}/api-keys", request, HttpMethod.Post, token);
}
public virtual async Task RevokeCurrentAPIKeyInfo(CancellationToken token = default)
{
var response = await _httpClient.SendAsync(CreateHttpRequest("api/v1/api-keys/current", null, HttpMethod.Delete), token);
await HandleResponse(response);
}
public virtual async Task RevokeCurrentAPIKeyInfo(CancellationToken token = default)
{
await SendHttpRequest("api/v1/api-keys/current", null, HttpMethod.Delete, token);
}
public virtual async Task RevokeAPIKey(string apikey, CancellationToken token = default)
{
if (apikey == null)
throw new ArgumentNullException(nameof(apikey));
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/api-keys/{apikey}", null, HttpMethod.Delete), token);
await HandleResponse(response);
}
public virtual async Task RevokeAPIKey(string userId, string apikey, CancellationToken token = default)
{
if (apikey == null)
throw new ArgumentNullException(nameof(apikey));
if (userId is null)
throw new ArgumentNullException(nameof(userId));
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/users/{userId}/api-keys/{apikey}", null, HttpMethod.Delete), token);
await HandleResponse(response);
}
public virtual async Task RevokeAPIKey(string apikey, CancellationToken token = default)
{
if (apikey == null) throw new ArgumentNullException(nameof(apikey));
await SendHttpRequest($"api/v1/api-keys/{apikey}", null, HttpMethod.Delete, token);
}
public virtual async Task RevokeAPIKey(string userId, string apikey, CancellationToken token = default)
{
if (apikey == null) throw new ArgumentNullException(nameof(apikey));
if (userId is null) throw new ArgumentNullException(nameof(userId));
await SendHttpRequest($"api/v1/users/{userId}/api-keys/{apikey}", null, HttpMethod.Delete, token);
}
}

@ -1,103 +1,81 @@
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Client.Models;
namespace BTCPayServer.Client
namespace BTCPayServer.Client;
public partial class BTCPayServerClient
{
public partial class BTCPayServerClient
public virtual async Task<PointOfSaleAppData> CreatePointOfSaleApp(string storeId,
PointOfSaleAppRequest request, CancellationToken token = default)
{
if (request == null) throw new ArgumentNullException(nameof(request));
return await SendHttpRequest<PointOfSaleAppData>($"api/v1/stores/{storeId}/apps/pos", request, HttpMethod.Post, token);
}
public virtual async Task<PointOfSaleAppData> CreatePointOfSaleApp(string storeId,
CreatePointOfSaleAppRequest request, CancellationToken token = default)
{
if (request == null)
throw new ArgumentNullException(nameof(request));
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/apps/pos", bodyPayload: request,
method: HttpMethod.Post), token);
return await HandleResponse<PointOfSaleAppData>(response);
}
public virtual async Task<CrowdfundAppData> CreateCrowdfundApp(string storeId,
CrowdfundAppRequest request, CancellationToken token = default)
{
if (request == null) throw new ArgumentNullException(nameof(request));
return await SendHttpRequest<CrowdfundAppData>($"api/v1/stores/{storeId}/apps/crowdfund", request, HttpMethod.Post, token);
}
public virtual async Task<CrowdfundAppData> CreateCrowdfundApp(string storeId,
CreateCrowdfundAppRequest request, CancellationToken token = default)
{
if (request == null)
throw new ArgumentNullException(nameof(request));
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/apps/crowdfund", bodyPayload: request,
method: HttpMethod.Post), token);
return await HandleResponse<CrowdfundAppData>(response);
}
public virtual async Task<PointOfSaleAppData> UpdatePointOfSaleApp(string appId,
PointOfSaleAppRequest request, CancellationToken token = default)
{
if (request == null) throw new ArgumentNullException(nameof(request));
return await SendHttpRequest<PointOfSaleAppData>($"api/v1/apps/pos/{appId}", request, HttpMethod.Put, token);
}
public virtual async Task<PointOfSaleAppData> UpdatePointOfSaleApp(string appId,
CreatePointOfSaleAppRequest request, CancellationToken token = default)
{
if (request == null)
throw new ArgumentNullException(nameof(request));
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/apps/pos/{appId}", bodyPayload: request,
method: HttpMethod.Put), token);
return await HandleResponse<PointOfSaleAppData>(response);
}
public virtual async Task<AppBaseData> GetApp(string appId, CancellationToken token = default)
{
if (appId == null) throw new ArgumentNullException(nameof(appId));
return await SendHttpRequest<AppBaseData>($"api/v1/apps/{appId}", null, HttpMethod.Get, token);
}
public virtual async Task<AppDataBase> GetApp(string appId, CancellationToken token = default)
{
if (appId == null)
throw new ArgumentNullException(nameof(appId));
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/apps/{appId}",
method: HttpMethod.Get), token);
return await HandleResponse<AppDataBase>(response);
}
public virtual async Task<AppBaseData[]> GetAllApps(string storeId, CancellationToken token = default)
{
if (storeId == null) throw new ArgumentNullException(nameof(storeId));
return await SendHttpRequest<AppBaseData[]>($"api/v1/stores/{storeId}/apps", null, HttpMethod.Get, token);
}
public virtual async Task<AppDataBase[]> GetAllApps(string storeId, CancellationToken token = default)
{
if (storeId == null)
throw new ArgumentNullException(nameof(storeId));
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/apps",
method: HttpMethod.Get), token);
return await HandleResponse<AppDataBase[]>(response);
}
public virtual async Task<AppBaseData[]> GetAllApps(CancellationToken token = default)
{
return await SendHttpRequest<AppBaseData[]>("api/v1/apps", null, HttpMethod.Get, token);
}
public virtual async Task<AppDataBase[]> GetAllApps(CancellationToken token = default)
{
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/apps",
method: HttpMethod.Get), token);
return await HandleResponse<AppDataBase[]>(response);
}
public virtual async Task<PointOfSaleAppData> GetPosApp(string appId, CancellationToken token = default)
{
if (appId == null) throw new ArgumentNullException(nameof(appId));
return await SendHttpRequest<PointOfSaleAppData>($"api/v1/apps/pos/{appId}", null, HttpMethod.Get, token);
}
public virtual async Task<PointOfSaleAppData> GetPosApp(string appId, CancellationToken token = default)
{
if (appId == null)
throw new ArgumentNullException(nameof(appId));
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/apps/pos/{appId}",
method: HttpMethod.Get), token);
return await HandleResponse<PointOfSaleAppData>(response);
}
public virtual async Task<CrowdfundAppData> GetCrowdfundApp(string appId, CancellationToken token = default)
{
if (appId == null) throw new ArgumentNullException(nameof(appId));
return await SendHttpRequest<CrowdfundAppData>($"api/v1/apps/crowdfund/{appId}", null, HttpMethod.Get, token);
}
public virtual async Task<CrowdfundAppData> GetCrowdfundApp(string appId, CancellationToken token = default)
{
if (appId == null)
throw new ArgumentNullException(nameof(appId));
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/apps/crowdfund/{appId}",
method: HttpMethod.Get), token);
return await HandleResponse<CrowdfundAppData>(response);
}
public virtual async Task<AppSalesStats> GetAppSales(string appId, int numberOfDays = 7, CancellationToken token = default)
{
if (appId == null) throw new ArgumentNullException(nameof(appId));
var queryPayload = new Dictionary<string, object> { { nameof(numberOfDays), numberOfDays } };
return await SendHttpRequest<AppSalesStats>($"api/v1/apps/{appId}/sales", queryPayload, HttpMethod.Get, token);
}
public virtual async Task DeleteApp(string appId, CancellationToken token = default)
{
if (appId == null)
throw new ArgumentNullException(nameof(appId));
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/apps/{appId}",
method: HttpMethod.Delete), token);
await HandleResponse(response);
}
public virtual async Task<List<AppItemStats>> GetAppTopItems(string appId, int offset = 0, int count = 10, CancellationToken token = default)
{
if (appId == null) throw new ArgumentNullException(nameof(appId));
var queryPayload = new Dictionary<string, object> { { nameof(offset), offset }, { nameof(count), count } };
return await SendHttpRequest<List<AppItemStats>>($"api/v1/apps/{appId}/top-items", queryPayload, HttpMethod.Get, token);
}
public virtual async Task DeleteApp(string appId, CancellationToken token = default)
{
if (appId == null) throw new ArgumentNullException(nameof(appId));
await SendHttpRequest($"api/v1/apps/{appId}", null, HttpMethod.Delete, token);
}
}

@ -1,34 +1,29 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace BTCPayServer.Client
namespace BTCPayServer.Client;
public partial class BTCPayServerClient
{
public partial class BTCPayServerClient
public static Uri GenerateAuthorizeUri(Uri btcpayHost, string[] permissions, bool strict = true,
bool selectiveStores = false, (string ApplicationIdentifier, Uri Redirect) applicationDetails = default)
{
public static Uri GenerateAuthorizeUri(Uri btcpayHost, string[] permissions, bool strict = true,
bool selectiveStores = false, (string ApplicationIdentifier, Uri Redirect) applicationDetails = default)
{
var result = new UriBuilder(btcpayHost);
result.Path = "api-keys/authorize";
AppendPayloadToQuery(result,
new Dictionary<string, object>()
{
{"strict", strict}, {"selectiveStores", selectiveStores}, {"permissions", permissions}
});
if (applicationDetails.Redirect != null)
var result = new UriBuilder(btcpayHost) { Path = "api-keys/authorize" };
AppendPayloadToQuery(result,
new Dictionary<string, object>
{
AppendPayloadToQuery(result, new KeyValuePair<string, object>("redirect", applicationDetails.Redirect));
if (!string.IsNullOrEmpty(applicationDetails.ApplicationIdentifier))
{
AppendPayloadToQuery(result, new KeyValuePair<string, object>("applicationIdentifier", applicationDetails.ApplicationIdentifier));
}
}
{"strict", strict}, {"selectiveStores", selectiveStores}, {"permissions", permissions}
});
return result.Uri;
if (applicationDetails.Redirect != null)
{
AppendPayloadToQuery(result, new KeyValuePair<string, object>("redirect", applicationDetails.Redirect));
if (!string.IsNullOrEmpty(applicationDetails.ApplicationIdentifier))
{
AppendPayloadToQuery(result, new KeyValuePair<string, object>("applicationIdentifier", applicationDetails.ApplicationIdentifier));
}
}
return result.Uri;
}
}

@ -1,102 +0,0 @@
using System.Collections.Generic;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Client.Models;
namespace BTCPayServer.Client
{
public partial class BTCPayServerClient
{
public virtual async Task<IEnumerable<CustodianAccountData>> GetCustodianAccounts(string storeId, bool includeAssetBalances = false, CancellationToken token = default)
{
var queryPayload = new Dictionary<string, object>();
if (includeAssetBalances)
{
queryPayload.Add("assetBalances", "true");
}
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/custodian-accounts", queryPayload), token);
return await HandleResponse<IEnumerable<CustodianAccountData>>(response);
}
public virtual async Task<CustodianAccountResponse> GetCustodianAccount(string storeId, string accountId, bool includeAssetBalances = false, CancellationToken token = default)
{
var queryPayload = new Dictionary<string, object>();
if (includeAssetBalances)
{
queryPayload.Add("assetBalances", "true");
}
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/custodian-accounts/{accountId}", queryPayload), token);
return await HandleResponse<CustodianAccountResponse>(response);
}
public virtual async Task<CustodianAccountData> CreateCustodianAccount(string storeId, CreateCustodianAccountRequest request, CancellationToken token = default)
{
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/custodian-accounts", bodyPayload: request, method: HttpMethod.Post), token);
return await HandleResponse<CustodianAccountData>(response);
}
public virtual async Task<CustodianAccountData> UpdateCustodianAccount(string storeId, string accountId, CreateCustodianAccountRequest request, CancellationToken token = default)
{
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/custodian-accounts/{accountId}", bodyPayload: request, method: HttpMethod.Put), token);
return await HandleResponse<CustodianAccountData>(response);
}
public virtual async Task DeleteCustodianAccount(string storeId, string accountId, CancellationToken token = default)
{
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/custodian-accounts/{accountId}", method: HttpMethod.Delete), token);
await HandleResponse(response);
}
public virtual async Task<DepositAddressData> GetCustodianAccountDepositAddress(string storeId, string accountId, string paymentMethod, CancellationToken token = default)
{
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/custodian-accounts/{accountId}/addresses/{paymentMethod}"), token);
return await HandleResponse<DepositAddressData>(response);
}
public virtual async Task<MarketTradeResponseData> MarketTradeCustodianAccountAsset(string storeId, string accountId, TradeRequestData request, CancellationToken token = default)
{
//var response = await _httpClient.SendAsync(CreateHttpRequest("api/v1/users", null, request, HttpMethod.Post), token);
//return await HandleResponse<ApplicationUserData>(response);
var internalRequest = CreateHttpRequest($"api/v1/stores/{storeId}/custodian-accounts/{accountId}/trades/market", null,
request, HttpMethod.Post);
var response = await _httpClient.SendAsync(internalRequest, token);
return await HandleResponse<MarketTradeResponseData>(response);
}
public virtual async Task<MarketTradeResponseData> GetCustodianAccountTradeInfo(string storeId, string accountId, string tradeId, CancellationToken token = default)
{
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/custodian-accounts/{accountId}/trades/{tradeId}", method: HttpMethod.Get), token);
return await HandleResponse<MarketTradeResponseData>(response);
}
public virtual async Task<TradeQuoteResponseData> GetCustodianAccountTradeQuote(string storeId, string accountId, string fromAsset, string toAsset, CancellationToken token = default)
{
var queryPayload = new Dictionary<string, object>();
queryPayload.Add("fromAsset", fromAsset);
queryPayload.Add("toAsset", toAsset);
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/custodian-accounts/{accountId}/trades/quote", queryPayload), token);
return await HandleResponse<TradeQuoteResponseData>(response);
}
public virtual async Task<WithdrawalResponseData> CreateCustodianAccountWithdrawal(string storeId, string accountId, WithdrawRequestData request, CancellationToken token = default)
{
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/custodian-accounts/{accountId}/withdrawals", bodyPayload: request, method: HttpMethod.Post), token);
return await HandleResponse<WithdrawalResponseData>(response);
}
public virtual async Task<WithdrawalSimulationResponseData> SimulateCustodianAccountWithdrawal(string storeId, string accountId, WithdrawRequestData request, CancellationToken token = default)
{
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/custodian-accounts/{accountId}/withdrawals/simulation", bodyPayload: request, method: HttpMethod.Post), token);
return await HandleResponse<WithdrawalSimulationResponseData>(response);
}
public virtual async Task<WithdrawalResponseData> GetCustodianAccountWithdrawalInfo(string storeId, string accountId, string paymentMethod, string withdrawalId, CancellationToken token = default)
{
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/custodian-accounts/{accountId}/withdrawals/{paymentMethod}/{withdrawalId}", method: HttpMethod.Get), token);
return await HandleResponse<WithdrawalResponseData>(response);
}
}
}

@ -1,16 +0,0 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Client.Models;
namespace BTCPayServer.Client
{
public partial class BTCPayServerClient
{
public virtual async Task<IEnumerable<CustodianData>> GetCustodians(CancellationToken token = default)
{
var response = await _httpClient.SendAsync(CreateHttpRequest("api/v1/custodians"), token);
return await HandleResponse<IEnumerable<CustodianData>>(response);
}
}
}

@ -0,0 +1,29 @@
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Client.Models;
namespace BTCPayServer.Client;
public partial class BTCPayServerClient
{
public virtual async Task<FileData[]> GetFiles(CancellationToken token = default)
{
return await SendHttpRequest<FileData[]>("api/v1/files", null, HttpMethod.Get, token);
}
public virtual async Task<FileData> GetFile(string fileId, CancellationToken token = default)
{
return await SendHttpRequest<FileData>($"api/v1/files/{fileId}", null, HttpMethod.Get, token);
}
public virtual async Task<FileData> UploadFile(string filePath, string mimeType, CancellationToken token = default)
{
return await UploadFileRequest<FileData>("api/v1/files", filePath, mimeType, "file", HttpMethod.Post, token);
}
public virtual async Task DeleteFile(string fileId, CancellationToken token = default)
{
await SendHttpRequest($"api/v1/files/{fileId}", null, HttpMethod.Delete, token);
}
}

@ -1,15 +1,14 @@
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Client.Models;
namespace BTCPayServer.Client
namespace BTCPayServer.Client;
public partial class BTCPayServerClient
{
public partial class BTCPayServerClient
public virtual async Task<ApiHealthData> GetHealth(CancellationToken token = default)
{
public virtual async Task<ApiHealthData> GetHealth(CancellationToken token = default)
{
var response = await _httpClient.SendAsync(CreateHttpRequest("api/v1/health"), token);
return await HandleResponse<ApiHealthData>(response);
}
return await SendHttpRequest<ApiHealthData>("api/v1/health", null, HttpMethod.Get, token);
}
}

@ -7,139 +7,101 @@ using System.Threading.Tasks;
using BTCPayServer.Client.Models;
using NBitcoin;
namespace BTCPayServer.Client
namespace BTCPayServer.Client;
public partial class BTCPayServerClient
{
public partial class BTCPayServerClient
public virtual async Task<IEnumerable<InvoiceData>> GetInvoices(string storeId, string[] orderId = null,
InvoiceStatus[] status = null,
DateTimeOffset? startDate = null,
DateTimeOffset? endDate = null,
string textSearch = null,
bool includeArchived = false,
int? skip = null,
int? take = null,
CancellationToken token = default)
{
public virtual async Task<IEnumerable<InvoiceData>> GetInvoices(string storeId, string[] orderId = null,
InvoiceStatus[] status = null,
DateTimeOffset? startDate = null,
DateTimeOffset? endDate = null,
string textSearch = null,
bool includeArchived = false,
int? skip = null,
int? take = null,
CancellationToken token = default)
var queryPayload = new Dictionary<string, object> { { nameof(includeArchived), includeArchived } };
if (startDate is { } s)
queryPayload.Add(nameof(startDate), Utils.DateTimeToUnixTime(s));
if (endDate is { } e)
queryPayload.Add(nameof(endDate), Utils.DateTimeToUnixTime(e));
if (orderId != null)
queryPayload.Add(nameof(orderId), orderId);
if (textSearch != null)
queryPayload.Add(nameof(textSearch), textSearch);
if (status != null)
queryPayload.Add(nameof(status), status.Select(s => s.ToString().ToLower()).ToArray());
if (skip != null)
queryPayload.Add(nameof(skip), skip);
if (take != null)
queryPayload.Add(nameof(take), take);
return await SendHttpRequest<IEnumerable<InvoiceData>>($"api/v1/stores/{storeId}/invoices", queryPayload, HttpMethod.Get, token);
}
public virtual async Task<InvoiceData> GetInvoice(string storeId, string invoiceId,
CancellationToken token = default)
{
return await SendHttpRequest<InvoiceData>($"api/v1/stores/{storeId}/invoices/{invoiceId}", null, HttpMethod.Get, token);
}
public virtual async Task<InvoicePaymentMethodDataModel[]> GetInvoicePaymentMethods(string storeId, string invoiceId,
bool onlyAccountedPayments = true, bool includeSensitive = false,
CancellationToken token = default)
{
var queryPayload = new Dictionary<string, object>
{
Dictionary<string, object> queryPayload = new Dictionary<string, object>();
queryPayload.Add(nameof(includeArchived), includeArchived);
{ nameof(onlyAccountedPayments), onlyAccountedPayments },
{ nameof(includeSensitive), includeSensitive }
};
return await SendHttpRequest<InvoicePaymentMethodDataModel[]>($"api/v1/stores/{storeId}/invoices/{invoiceId}/payment-methods", queryPayload, HttpMethod.Get, token);
}
if (startDate is DateTimeOffset s)
queryPayload.Add(nameof(startDate), Utils.DateTimeToUnixTime(s));
public virtual async Task ArchiveInvoice(string storeId, string invoiceId,
CancellationToken token = default)
{
await SendHttpRequest($"api/v1/stores/{storeId}/invoices/{invoiceId}", null, HttpMethod.Delete, token);
}
if (endDate is DateTimeOffset e)
queryPayload.Add(nameof(endDate), Utils.DateTimeToUnixTime(e));
public virtual async Task<InvoiceData> CreateInvoice(string storeId,
CreateInvoiceRequest request, CancellationToken token = default)
{
if (request == null) throw new ArgumentNullException(nameof(request));
return await SendHttpRequest<InvoiceData>($"api/v1/stores/{storeId}/invoices", request, HttpMethod.Post, token);
}
if (orderId != null)
queryPayload.Add(nameof(orderId), orderId);
if (textSearch != null)
queryPayload.Add(nameof(textSearch), textSearch);
if (status != null)
queryPayload.Add(nameof(status), status.Select(s => s.ToString().ToLower()).ToArray());
public virtual async Task<InvoiceData> UpdateInvoice(string storeId, string invoiceId,
UpdateInvoiceRequest request, CancellationToken token = default)
{
if (request == null) throw new ArgumentNullException(nameof(request));
return await SendHttpRequest<InvoiceData>($"api/v1/stores/{storeId}/invoices/{invoiceId}", request, HttpMethod.Put, token);
}
if (skip != null)
{
queryPayload.Add(nameof(skip), skip);
}
public virtual async Task<InvoiceData> MarkInvoiceStatus(string storeId, string invoiceId,
MarkInvoiceStatusRequest request, CancellationToken token = default)
{
if (request == null) throw new ArgumentNullException(nameof(request));
if (request.Status != InvoiceStatus.Settled && request.Status != InvoiceStatus.Invalid) throw new ArgumentOutOfRangeException(nameof(request.Status), "Status can only be Invalid or Complete");
return await SendHttpRequest<InvoiceData>($"api/v1/stores/{storeId}/invoices/{invoiceId}/status", request, HttpMethod.Post, token);
}
if (take != null)
{
queryPayload.Add(nameof(take), take);
}
public virtual async Task<InvoiceData> UnarchiveInvoice(string storeId, string invoiceId, CancellationToken token = default)
{
return await SendHttpRequest<InvoiceData>($"api/v1/stores/{storeId}/invoices/{invoiceId}/unarchive", null, HttpMethod.Post, token);
}
var response =
await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/invoices",
queryPayload), token);
return await HandleResponse<IEnumerable<InvoiceData>>(response);
}
public virtual async Task ActivateInvoicePaymentMethod(string storeId, string invoiceId, string paymentMethod, CancellationToken token = default)
{
await SendHttpRequest($"api/v1/stores/{storeId}/invoices/{invoiceId}/payment-methods/{paymentMethod}/activate", null, HttpMethod.Post, token);
}
public virtual async Task<InvoiceData> GetInvoice(string storeId, string invoiceId,
CancellationToken token = default)
{
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/invoices/{invoiceId}"), token);
return await HandleResponse<InvoiceData>(response);
}
public virtual async Task<InvoicePaymentMethodDataModel[]> GetInvoicePaymentMethods(string storeId, string invoiceId,
CancellationToken token = default)
{
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/invoices/{invoiceId}/payment-methods"), token);
return await HandleResponse<InvoicePaymentMethodDataModel[]>(response);
}
public virtual async Task ArchiveInvoice(string storeId, string invoiceId,
CancellationToken token = default)
{
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/invoices/{invoiceId}",
method: HttpMethod.Delete), token);
await HandleResponse(response);
}
public virtual async Task<InvoiceData> CreateInvoice(string storeId,
CreateInvoiceRequest request, CancellationToken token = default)
{
if (request == null)
throw new ArgumentNullException(nameof(request));
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/invoices", bodyPayload: request,
method: HttpMethod.Post), token);
return await HandleResponse<InvoiceData>(response);
}
public virtual async Task<InvoiceData> UpdateInvoice(string storeId, string invoiceId,
UpdateInvoiceRequest request, CancellationToken token = default)
{
if (request == null)
throw new ArgumentNullException(nameof(request));
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/invoices/{invoiceId}", bodyPayload: request,
method: HttpMethod.Put), token);
return await HandleResponse<InvoiceData>(response);
}
public virtual async Task<InvoiceData> MarkInvoiceStatus(string storeId, string invoiceId,
MarkInvoiceStatusRequest request, CancellationToken token = default)
{
if (request == null)
throw new ArgumentNullException(nameof(request));
if (request.Status != InvoiceStatus.Settled && request.Status != InvoiceStatus.Invalid)
throw new ArgumentOutOfRangeException(nameof(request.Status), "Status can only be Invalid or Complete");
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/invoices/{invoiceId}/status", bodyPayload: request,
method: HttpMethod.Post), token);
return await HandleResponse<InvoiceData>(response);
}
public virtual async Task<InvoiceData> UnarchiveInvoice(string storeId, string invoiceId, CancellationToken token = default)
{
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/invoices/{invoiceId}/unarchive",
method: HttpMethod.Post), token);
return await HandleResponse<InvoiceData>(response);
}
public virtual async Task ActivateInvoicePaymentMethod(string storeId, string invoiceId, string paymentMethod, CancellationToken token = default)
{
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/invoices/{invoiceId}/payment-methods/{paymentMethod}/activate",
method: HttpMethod.Post), token);
await HandleResponse(response);
}
public virtual async Task<PullPaymentData> RefundInvoice(
string storeId,
string invoiceId,
RefundInvoiceRequest request,
CancellationToken token = default
)
{
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/invoices/{invoiceId}/refund", bodyPayload: request,
method: HttpMethod.Post), token);
return await HandleResponse<PullPaymentData>(response);
}
public virtual async Task<PullPaymentData> RefundInvoice(
string storeId,
string invoiceId,
RefundInvoiceRequest request,
CancellationToken token = default
)
{
return await SendHttpRequest<PullPaymentData>($"api/v1/stores/{storeId}/invoices/{invoiceId}/refund", request, HttpMethod.Post, token);
}
}

@ -1,59 +0,0 @@
using System.Collections.Generic;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Client.Models;
namespace BTCPayServer.Client
{
public partial class BTCPayServerClient
{
public virtual async Task<IEnumerable<LNURLPayPaymentMethodData>>
GetStoreLNURLPayPaymentMethods(string storeId, bool? enabled = null,
CancellationToken token = default)
{
var query = new Dictionary<string, object>();
if (enabled != null)
{
query.Add(nameof(enabled), enabled);
}
var response =
await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/LNURLPay",
query), token);
return await HandleResponse<IEnumerable<LNURLPayPaymentMethodData>>(response);
}
public virtual async Task<LNURLPayPaymentMethodData> GetStoreLNURLPayPaymentMethod(
string storeId,
string cryptoCode, CancellationToken token = default)
{
var response =
await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/LNURLPay/{cryptoCode}"), token);
return await HandleResponse<LNURLPayPaymentMethodData>(response);
}
public virtual async Task RemoveStoreLNURLPayPaymentMethod(string storeId,
string cryptoCode, CancellationToken token = default)
{
var response =
await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/LNURLPay/{cryptoCode}",
method: HttpMethod.Delete), token);
await HandleResponse(response);
}
public virtual async Task<LNURLPayPaymentMethodData> UpdateStoreLNURLPayPaymentMethod(
string storeId,
string cryptoCode, LNURLPayPaymentMethodData paymentMethod,
CancellationToken token = default)
{
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/LNURLPay/{cryptoCode}",
bodyPayload: paymentMethod, method: HttpMethod.Put), token);
return await HandleResponse<LNURLPayPaymentMethodData>(response);
}
}
}

@ -5,142 +5,101 @@ using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Client.Models;
namespace BTCPayServer.Client
namespace BTCPayServer.Client;
public partial class BTCPayServerClient
{
public partial class BTCPayServerClient
public virtual async Task<LightningNodeInformationData> GetLightningNodeInfo(string cryptoCode,
CancellationToken token = default)
{
public virtual async Task<LightningNodeInformationData> GetLightningNodeInfo(string cryptoCode,
CancellationToken token = default)
return await SendHttpRequest<LightningNodeInformationData>($"api/v1/server/lightning/{cryptoCode}/info", null, HttpMethod.Get, token);
}
public virtual async Task<LightningNodeBalanceData> GetLightningNodeBalance(string cryptoCode,
CancellationToken token = default)
{
return await SendHttpRequest<LightningNodeBalanceData>($"api/v1/server/lightning/{cryptoCode}/balance", null, HttpMethod.Get, token);
}
public virtual async Task ConnectToLightningNode(string cryptoCode, ConnectToNodeRequest request,
CancellationToken token = default)
{
if (request == null) throw new ArgumentNullException(nameof(request));
await SendHttpRequest($"api/v1/server/lightning/{cryptoCode}/connect", request, HttpMethod.Post, token);
}
public virtual async Task<IEnumerable<LightningChannelData>> GetLightningNodeChannels(string cryptoCode,
CancellationToken token = default)
{
return await SendHttpRequest<IEnumerable<LightningChannelData>>($"api/v1/server/lightning/{cryptoCode}/channels", null, HttpMethod.Get, token);
}
public virtual async Task OpenLightningChannel(string cryptoCode, OpenLightningChannelRequest request,
CancellationToken token = default)
{
await SendHttpRequest($"api/v1/server/lightning/{cryptoCode}/channels", request, HttpMethod.Post, token);
}
public virtual async Task<string> GetLightningDepositAddress(string cryptoCode, CancellationToken token = default)
{
return await SendHttpRequest<string>($"api/v1/server/lightning/{cryptoCode}/address", null, HttpMethod.Post, token);
}
public virtual async Task<LightningPaymentData> PayLightningInvoice(string cryptoCode, PayLightningInvoiceRequest request,
CancellationToken token = default)
{
if (request == null) throw new ArgumentNullException(nameof(request));
return await SendHttpRequest<LightningPaymentData>($"api/v1/server/lightning/{cryptoCode}/invoices/pay", request, HttpMethod.Post, token);
}
public virtual async Task<LightningPaymentData> GetLightningPayment(string cryptoCode,
string paymentHash, CancellationToken token = default)
{
if (paymentHash == null) throw new ArgumentNullException(nameof(paymentHash));
return await SendHttpRequest<LightningPaymentData>($"api/v1/server/lightning/{cryptoCode}/payments/{paymentHash}", null, HttpMethod.Get, token);
}
public virtual async Task<LightningInvoiceData> GetLightningInvoice(string cryptoCode,
string invoiceId, CancellationToken token = default)
{
if (invoiceId == null) throw new ArgumentNullException(nameof(invoiceId));
return await SendHttpRequest<LightningInvoiceData>($"api/v1/server/lightning/{cryptoCode}/invoices/{invoiceId}", null, HttpMethod.Get, token);
}
public virtual async Task<LightningInvoiceData[]> GetLightningInvoices(string cryptoCode,
bool? pendingOnly = null, long? offsetIndex = null, CancellationToken token = default)
{
var queryPayload = new Dictionary<string, object>();
if (pendingOnly is bool v)
{
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/server/lightning/{cryptoCode}/info",
method: HttpMethod.Get), token);
return await HandleResponse<LightningNodeInformationData>(response);
queryPayload.Add("pendingOnly", v.ToString());
}
public virtual async Task<LightningNodeBalanceData> GetLightningNodeBalance(string cryptoCode,
CancellationToken token = default)
if (offsetIndex is > 0)
{
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/server/lightning/{cryptoCode}/balance",
method: HttpMethod.Get), token);
return await HandleResponse<LightningNodeBalanceData>(response);
queryPayload.Add("offsetIndex", offsetIndex);
}
return await SendHttpRequest<LightningInvoiceData[]>($"api/v1/server/lightning/{cryptoCode}/invoices", queryPayload, HttpMethod.Get, token);
}
public virtual async Task ConnectToLightningNode(string cryptoCode, ConnectToNodeRequest request,
CancellationToken token = default)
public virtual async Task<LightningPaymentData[]> GetLightningPayments(string cryptoCode,
bool? includePending = null, long? offsetIndex = null, CancellationToken token = default)
{
var queryPayload = new Dictionary<string, object>();
if (includePending is bool v)
{
if (request == null)
throw new ArgumentNullException(nameof(request));
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/server/lightning/{cryptoCode}/connect", bodyPayload: request,
method: HttpMethod.Post), token);
await HandleResponse(response);
queryPayload.Add("includePending", v.ToString());
}
public virtual async Task<IEnumerable<LightningChannelData>> GetLightningNodeChannels(string cryptoCode,
CancellationToken token = default)
if (offsetIndex is > 0)
{
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/server/lightning/{cryptoCode}/channels",
method: HttpMethod.Get), token);
return await HandleResponse<IEnumerable<LightningChannelData>>(response);
queryPayload.Add("offsetIndex", offsetIndex);
}
return await SendHttpRequest<LightningPaymentData[]>($"api/v1/server/lightning/{cryptoCode}/payments", queryPayload, HttpMethod.Get, token);
}
public virtual async Task OpenLightningChannel(string cryptoCode, OpenLightningChannelRequest request,
CancellationToken token = default)
{
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/server/lightning/{cryptoCode}/channels", bodyPayload: request,
method: HttpMethod.Post), token);
await HandleResponse(response);
}
public virtual async Task<string> GetLightningDepositAddress(string cryptoCode, CancellationToken token = default)
{
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/server/lightning/{cryptoCode}/address", method: HttpMethod.Post), token);
return await HandleResponse<string>(response);
}
public virtual async Task<LightningPaymentData> PayLightningInvoice(string cryptoCode, PayLightningInvoiceRequest request,
CancellationToken token = default)
{
if (request == null)
throw new ArgumentNullException(nameof(request));
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/server/lightning/{cryptoCode}/invoices/pay", bodyPayload: request,
method: HttpMethod.Post), token);
return await HandleResponse<LightningPaymentData>(response);
}
public virtual async Task<LightningPaymentData> GetLightningPayment(string cryptoCode,
string paymentHash, CancellationToken token = default)
{
if (paymentHash == null)
throw new ArgumentNullException(nameof(paymentHash));
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/server/lightning/{cryptoCode}/payments/{paymentHash}",
method: HttpMethod.Get), token);
return await HandleResponse<LightningPaymentData>(response);
}
public virtual async Task<LightningInvoiceData> GetLightningInvoice(string cryptoCode,
string invoiceId, CancellationToken token = default)
{
if (invoiceId == null)
throw new ArgumentNullException(nameof(invoiceId));
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/server/lightning/{cryptoCode}/invoices/{invoiceId}",
method: HttpMethod.Get), token);
return await HandleResponse<LightningInvoiceData>(response);
}
public virtual async Task<LightningInvoiceData[]> GetLightningInvoices(string cryptoCode,
bool? pendingOnly = null, long? offsetIndex = null, CancellationToken token = default)
{
var queryPayload = new Dictionary<string, object>();
if (pendingOnly is bool v)
{
queryPayload.Add("pendingOnly", v.ToString());
}
if (offsetIndex is > 0)
{
queryPayload.Add("offsetIndex", offsetIndex);
}
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/server/lightning/{cryptoCode}/invoices", queryPayload), token);
return await HandleResponse<LightningInvoiceData[]>(response);
}
public virtual async Task<LightningPaymentData[]> GetLightningPayments(string cryptoCode,
bool? includePending = null, long? offsetIndex = null, CancellationToken token = default)
{
var queryPayload = new Dictionary<string, object>();
if (includePending is bool v)
{
queryPayload.Add("includePending", v.ToString());
}
if (offsetIndex is > 0)
{
queryPayload.Add("offsetIndex", offsetIndex);
}
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/server/lightning/{cryptoCode}/payments", queryPayload), token);
return await HandleResponse<LightningPaymentData[]>(response);
}
public virtual async Task<LightningInvoiceData> CreateLightningInvoice(string cryptoCode, CreateLightningInvoiceRequest request,
CancellationToken token = default)
{
if (request == null)
throw new ArgumentNullException(nameof(request));
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/server/lightning/{cryptoCode}/invoices", bodyPayload: request,
method: HttpMethod.Post), token);
return await HandleResponse<LightningInvoiceData>(response);
}
public virtual async Task<LightningInvoiceData> CreateLightningInvoice(string cryptoCode, CreateLightningInvoiceRequest request,
CancellationToken token = default)
{
if (request == null) throw new ArgumentNullException(nameof(request));
return await SendHttpRequest<LightningInvoiceData>($"api/v1/server/lightning/{cryptoCode}/invoices", request, HttpMethod.Post, token);
}
}

@ -5,144 +5,102 @@ using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Client.Models;
namespace BTCPayServer.Client
namespace BTCPayServer.Client;
public partial class BTCPayServerClient
{
public partial class BTCPayServerClient
public virtual async Task<LightningNodeInformationData> GetLightningNodeInfo(string storeId, string cryptoCode,
CancellationToken token = default)
{
public virtual async Task<LightningNodeInformationData> GetLightningNodeInfo(string storeId, string cryptoCode,
CancellationToken token = default)
return await SendHttpRequest<LightningNodeInformationData>($"api/v1/stores/{storeId}/lightning/{cryptoCode}/info", null, HttpMethod.Get, token);
}
public virtual async Task<LightningNodeBalanceData> GetLightningNodeBalance(string storeId, string cryptoCode,
CancellationToken token = default)
{
return await SendHttpRequest<LightningNodeBalanceData>($"api/v1/stores/{storeId}/lightning/{cryptoCode}/balance", null, HttpMethod.Get, token);
}
public virtual async Task ConnectToLightningNode(string storeId, string cryptoCode, ConnectToNodeRequest request,
CancellationToken token = default)
{
if (request == null) throw new ArgumentNullException(nameof(request));
await SendHttpRequest($"api/v1/stores/{storeId}/lightning/{cryptoCode}/connect", request, HttpMethod.Post, token);
}
public virtual async Task<IEnumerable<LightningChannelData>> GetLightningNodeChannels(string storeId, string cryptoCode,
CancellationToken token = default)
{
return await SendHttpRequest<IEnumerable<LightningChannelData>>($"api/v1/stores/{storeId}/lightning/{cryptoCode}/channels", null, HttpMethod.Get, token);
}
public virtual async Task OpenLightningChannel(string storeId, string cryptoCode, OpenLightningChannelRequest request,
CancellationToken token = default)
{
await SendHttpRequest($"api/v1/stores/{storeId}/lightning/{cryptoCode}/channels", request, HttpMethod.Post, token);
}
public virtual async Task<string> GetLightningDepositAddress(string storeId, string cryptoCode,
CancellationToken token = default)
{
return await SendHttpRequest<string>($"api/v1/stores/{storeId}/lightning/{cryptoCode}/address", null, HttpMethod.Post, token);
}
public virtual async Task<LightningPaymentData> PayLightningInvoice(string storeId, string cryptoCode, PayLightningInvoiceRequest request,
CancellationToken token = default)
{
if (request == null) throw new ArgumentNullException(nameof(request));
return await SendHttpRequest<LightningPaymentData>($"api/v1/stores/{storeId}/lightning/{cryptoCode}/invoices/pay", request, HttpMethod.Post, token);
}
public virtual async Task<LightningPaymentData> GetLightningPayment(string storeId, string cryptoCode,
string paymentHash, CancellationToken token = default)
{
if (paymentHash == null) throw new ArgumentNullException(nameof(paymentHash));
return await SendHttpRequest<LightningPaymentData>($"api/v1/stores/{storeId}/lightning/{cryptoCode}/payments/{paymentHash}", null, HttpMethod.Get, token);
}
public virtual async Task<LightningInvoiceData> GetLightningInvoice(string storeId, string cryptoCode,
string invoiceId, CancellationToken token = default)
{
if (invoiceId == null) throw new ArgumentNullException(nameof(invoiceId));
return await SendHttpRequest<LightningInvoiceData>($"api/v1/stores/{storeId}/lightning/{cryptoCode}/invoices/{invoiceId}", null, HttpMethod.Get, token);
}
public virtual async Task<LightningInvoiceData[]> GetLightningInvoices(string storeId, string cryptoCode,
bool? pendingOnly = null, long? offsetIndex = null, CancellationToken token = default)
{
var queryPayload = new Dictionary<string, object>();
if (pendingOnly is bool v)
{
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/lightning/{cryptoCode}/info",
method: HttpMethod.Get), token);
return await HandleResponse<LightningNodeInformationData>(response);
queryPayload.Add("pendingOnly", v.ToString());
}
public virtual async Task<LightningNodeBalanceData> GetLightningNodeBalance(string storeId, string cryptoCode,
CancellationToken token = default)
if (offsetIndex is > 0)
{
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/lightning/{cryptoCode}/balance",
method: HttpMethod.Get), token);
return await HandleResponse<LightningNodeBalanceData>(response);
queryPayload.Add("offsetIndex", offsetIndex);
}
return await SendHttpRequest<LightningInvoiceData[]>($"api/v1/stores/{storeId}/lightning/{cryptoCode}/invoices", queryPayload, HttpMethod.Get, token);
}
public virtual async Task ConnectToLightningNode(string storeId, string cryptoCode, ConnectToNodeRequest request,
CancellationToken token = default)
public virtual async Task<LightningPaymentData[]> GetLightningPayments(string storeId, string cryptoCode,
bool? includePending = null, long? offsetIndex = null, CancellationToken token = default)
{
var queryPayload = new Dictionary<string, object>();
if (includePending is bool v)
{
if (request == null)
throw new ArgumentNullException(nameof(request));
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/lightning/{cryptoCode}/connect", bodyPayload: request,
method: HttpMethod.Post), token);
await HandleResponse(response);
queryPayload.Add("includePending", v.ToString());
}
public virtual async Task<IEnumerable<LightningChannelData>> GetLightningNodeChannels(string storeId, string cryptoCode,
CancellationToken token = default)
if (offsetIndex is > 0)
{
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/lightning/{cryptoCode}/channels",
method: HttpMethod.Get), token);
return await HandleResponse<IEnumerable<LightningChannelData>>(response);
queryPayload.Add("offsetIndex", offsetIndex);
}
return await SendHttpRequest<LightningPaymentData[]>($"api/v1/stores/{storeId}/lightning/{cryptoCode}/payments", queryPayload, HttpMethod.Get, token);
}
public virtual async Task OpenLightningChannel(string storeId, string cryptoCode, OpenLightningChannelRequest request,
CancellationToken token = default)
{
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/lightning/{cryptoCode}/channels", bodyPayload: request,
method: HttpMethod.Post), token);
await HandleResponse(response);
}
public virtual async Task<string> GetLightningDepositAddress(string storeId, string cryptoCode,
CancellationToken token = default)
{
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/lightning/{cryptoCode}/address", method: HttpMethod.Post),
token);
return await HandleResponse<string>(response);
}
public virtual async Task<LightningPaymentData> PayLightningInvoice(string storeId, string cryptoCode, PayLightningInvoiceRequest request,
CancellationToken token = default)
{
if (request == null)
throw new ArgumentNullException(nameof(request));
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/lightning/{cryptoCode}/invoices/pay", bodyPayload: request,
method: HttpMethod.Post), token);
return await HandleResponse<LightningPaymentData>(response);
}
public virtual async Task<LightningPaymentData> GetLightningPayment(string storeId, string cryptoCode,
string paymentHash, CancellationToken token = default)
{
if (paymentHash == null)
throw new ArgumentNullException(nameof(paymentHash));
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/lightning/{cryptoCode}/payments/{paymentHash}",
method: HttpMethod.Get), token);
return await HandleResponse<LightningPaymentData>(response);
}
public virtual async Task<LightningInvoiceData> GetLightningInvoice(string storeId, string cryptoCode,
string invoiceId, CancellationToken token = default)
{
if (invoiceId == null)
throw new ArgumentNullException(nameof(invoiceId));
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/lightning/{cryptoCode}/invoices/{invoiceId}",
method: HttpMethod.Get), token);
return await HandleResponse<LightningInvoiceData>(response);
}
public virtual async Task<LightningInvoiceData[]> GetLightningInvoices(string storeId, string cryptoCode,
bool? pendingOnly = null, long? offsetIndex = null, CancellationToken token = default)
{
var queryPayload = new Dictionary<string, object>();
if (pendingOnly is bool v)
{
queryPayload.Add("pendingOnly", v.ToString());
}
if (offsetIndex is > 0)
{
queryPayload.Add("offsetIndex", offsetIndex);
}
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/lightning/{cryptoCode}/invoices", queryPayload), token);
return await HandleResponse<LightningInvoiceData[]>(response);
}
public virtual async Task<LightningPaymentData[]> GetLightningPayments(string storeId, string cryptoCode,
bool? includePending = null, long? offsetIndex = null, CancellationToken token = default)
{
var queryPayload = new Dictionary<string, object>();
if (includePending is bool v)
{
queryPayload.Add("includePending", v.ToString());
}
if (offsetIndex is > 0)
{
queryPayload.Add("offsetIndex", offsetIndex);
}
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/lightning/{cryptoCode}/payments", queryPayload), token);
return await HandleResponse<LightningPaymentData[]>(response);
}
public virtual async Task<LightningInvoiceData> CreateLightningInvoice(string storeId, string cryptoCode,
CreateLightningInvoiceRequest request, CancellationToken token = default)
{
if (request == null)
throw new ArgumentNullException(nameof(request));
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/lightning/{cryptoCode}/invoices", bodyPayload: request,
method: HttpMethod.Post), token);
return await HandleResponse<LightningInvoiceData>(response);
}
public virtual async Task<LightningInvoiceData> CreateLightningInvoice(string storeId, string cryptoCode,
CreateLightningInvoiceRequest request, CancellationToken token = default)
{
if (request == null) throw new ArgumentNullException(nameof(request));
return await SendHttpRequest<LightningInvoiceData>($"api/v1/stores/{storeId}/lightning/{cryptoCode}/invoices", request, HttpMethod.Post, token);
}
}

@ -3,46 +3,32 @@ using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Client.Models;
namespace BTCPayServer.Client
namespace BTCPayServer.Client;
public partial class BTCPayServerClient
{
public partial class BTCPayServerClient
public virtual async Task<LightningAddressData[]> GetStoreLightningAddresses(string storeId,
CancellationToken token = default)
{
public virtual async Task<LightningAddressData[]> GetStoreLightningAddresses(string storeId,
CancellationToken token = default)
{
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/lightning-addresses",
method: HttpMethod.Get), token);
return await HandleResponse<LightningAddressData[]>(response);
}
return await SendHttpRequest<LightningAddressData[]>($"api/v1/stores/{storeId}/lightning-addresses", null, HttpMethod.Get, token);
}
public virtual async Task<LightningAddressData> GetStoreLightningAddress(string storeId, string username,
CancellationToken token = default)
{
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/lightning-addresses/{username}",
method: HttpMethod.Get), token);
return await HandleResponse<LightningAddressData>(response);
}
public virtual async Task<LightningAddressData> GetStoreLightningAddress(string storeId, string username,
CancellationToken token = default)
{
return await SendHttpRequest<LightningAddressData>($"api/v1/stores/{storeId}/lightning-addresses/{username}", null, HttpMethod.Get, token);
}
public virtual async Task RemoveStoreLightningAddress(string storeId, string username,
CancellationToken token = default)
{
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/lightning-addresses/{username}",
method: HttpMethod.Delete), token);
await HandleResponse(response);
}
public virtual async Task RemoveStoreLightningAddress(string storeId, string username,
CancellationToken token = default)
{
await SendHttpRequest($"api/v1/stores/{storeId}/lightning-addresses/{username}", null, HttpMethod.Delete, token);
}
public virtual async Task<LightningAddressData> AddOrUpdateStoreLightningAddress(string storeId,
string username, LightningAddressData data,
CancellationToken token = default)
{
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/lightning-addresses/{username}",
method: HttpMethod.Post, bodyPayload: data), token);
return await HandleResponse<LightningAddressData>(response);
}
public virtual async Task<LightningAddressData> AddOrUpdateStoreLightningAddress(string storeId,
string username, LightningAddressData data,
CancellationToken token = default)
{
return await SendHttpRequest<LightningAddressData>($"api/v1/stores/{storeId}/lightning-addresses/{username}", data, HttpMethod.Post, token);
}
}

@ -1,59 +0,0 @@
using System.Collections.Generic;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Client.Models;
namespace BTCPayServer.Client
{
public partial class BTCPayServerClient
{
public virtual async Task<IEnumerable<LightningNetworkPaymentMethodData>>
GetStoreLightningNetworkPaymentMethods(string storeId, bool? enabled = null,
CancellationToken token = default)
{
var query = new Dictionary<string, object>();
if (enabled != null)
{
query.Add(nameof(enabled), enabled);
}
var response =
await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/LightningNetwork",
query), token);
return await HandleResponse<IEnumerable<LightningNetworkPaymentMethodData>>(response);
}
public virtual async Task<LightningNetworkPaymentMethodData> GetStoreLightningNetworkPaymentMethod(
string storeId,
string cryptoCode, CancellationToken token = default)
{
var response =
await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/LightningNetwork/{cryptoCode}"), token);
return await HandleResponse<LightningNetworkPaymentMethodData>(response);
}
public virtual async Task RemoveStoreLightningNetworkPaymentMethod(string storeId,
string cryptoCode, CancellationToken token = default)
{
var response =
await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/LightningNetwork/{cryptoCode}",
method: HttpMethod.Delete), token);
await HandleResponse(response);
}
public virtual async Task<LightningNetworkPaymentMethodData> UpdateStoreLightningNetworkPaymentMethod(
string storeId,
string cryptoCode, UpdateLightningNetworkPaymentMethodRequest paymentMethod,
CancellationToken token = default)
{
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/LightningNetwork/{cryptoCode}",
bodyPayload: paymentMethod, method: HttpMethod.Put), token);
return await HandleResponse<LightningNetworkPaymentMethodData>(response);
}
}
}

@ -1,23 +1,19 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Client.Models;
namespace BTCPayServer.Client
namespace BTCPayServer.Client;
public partial class BTCPayServerClient
{
public partial class BTCPayServerClient
public virtual async Task<PermissionMetadata[]> GetPermissionMetadata(CancellationToken token = default)
{
public virtual async Task<PermissionMetadata[]> GetPermissionMetadata(CancellationToken token = default)
{
var response = await _httpClient.SendAsync(CreateHttpRequest("misc/permissions"), token);
return await HandleResponse<PermissionMetadata[]>(response);
}
public virtual async Task<Language[]> GetAvailableLanguages(CancellationToken token = default)
{
var response = await _httpClient.SendAsync(CreateHttpRequest("misc/lang"), token);
return await HandleResponse<Language[]>(response);
}
return await SendHttpRequest<PermissionMetadata[]>("misc/permissions", null, HttpMethod.Get, token);
}
public virtual async Task<Language[]> GetAvailableLanguages(CancellationToken token = default)
{
return await SendHttpRequest<Language[]>("misc/lang", null, HttpMethod.Get, token);
}
}

@ -1,56 +1,52 @@
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Client.Models;
namespace BTCPayServer.Client
namespace BTCPayServer.Client;
public partial class BTCPayServerClient
{
public partial class BTCPayServerClient
public virtual async Task<IEnumerable<NotificationData>> GetNotifications(bool? seen = null, int? skip = null,
int? take = null, string[] storeId = null, CancellationToken token = default)
{
public virtual async Task<IEnumerable<NotificationData>> GetNotifications(bool? seen = null, int? skip = null,
int? take = null, CancellationToken token = default)
{
Dictionary<string, object> queryPayload = new Dictionary<string, object>();
var queryPayload = new Dictionary<string, object>();
if (seen != null)
queryPayload.Add(nameof(seen), seen);
if (skip != null)
queryPayload.Add(nameof(skip), skip);
if (take != null)
queryPayload.Add(nameof(take), take);
if (storeId != null)
queryPayload.Add(nameof(storeId), storeId);
return await SendHttpRequest<IEnumerable<NotificationData>>("api/v1/users/me/notifications", queryPayload, HttpMethod.Get, token);
}
if (seen != null)
queryPayload.Add(nameof(seen), seen);
if (skip != null)
queryPayload.Add(nameof(skip), skip);
if (take != null)
queryPayload.Add(nameof(take), take);
public virtual async Task<NotificationData> GetNotification(string notificationId,
CancellationToken token = default)
{
return await SendHttpRequest<NotificationData>($"api/v1/users/me/notifications/{notificationId}", null, HttpMethod.Get, token);
}
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/users/me/notifications",
queryPayload), token);
public virtual async Task<NotificationData> UpdateNotification(string notificationId, bool? seen,
CancellationToken token = default)
{
return await SendHttpRequest<NotificationData>($"api/v1/users/me/notifications/{notificationId}", new UpdateNotification { Seen = seen }, HttpMethod.Put, token);
}
return await HandleResponse<IEnumerable<NotificationData>>(response);
}
public virtual async Task<NotificationSettingsData> GetNotificationSettings(CancellationToken token = default)
{
return await SendHttpRequest<NotificationSettingsData>("api/v1/users/me/notification-settings", null, HttpMethod.Get, token);
}
public virtual async Task<NotificationData> GetNotification(string notificationId,
CancellationToken token = default)
{
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/users/me/notifications/{notificationId}"), token);
return await HandleResponse<NotificationData>(response);
}
public virtual async Task<NotificationSettingsData> UpdateNotificationSettings(UpdateNotificationSettingsRequest request, CancellationToken token = default)
{
return await SendHttpRequest<NotificationSettingsData>("api/v1/users/me/notification-settings", request, HttpMethod.Put, token);
}
public virtual async Task<NotificationData> UpdateNotification(string notificationId, bool? seen,
CancellationToken token = default)
{
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/users/me/notifications/{notificationId}",
method: HttpMethod.Put, bodyPayload: new UpdateNotification() { Seen = seen }), token);
return await HandleResponse<NotificationData>(response);
}
public virtual async Task RemoveNotification(string notificationId, CancellationToken token = default)
{
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/users/me/notifications/{notificationId}",
method: HttpMethod.Delete), token);
await HandleResponse(response);
}
public virtual async Task RemoveNotification(string notificationId, CancellationToken token = default)
{
await SendHttpRequest($"api/v1/users/me/notifications/{notificationId}", null, HttpMethod.Delete, token);
}
}

@ -3,92 +3,37 @@ using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Client.Models;
using Newtonsoft.Json.Linq;
namespace BTCPayServer.Client
namespace BTCPayServer.Client;
public partial class BTCPayServerClient
{
public partial class BTCPayServerClient
public virtual async Task<OnChainPaymentMethodPreviewResultData>
PreviewProposedStoreOnChainPaymentMethodAddresses(
string storeId, string paymentMethodId, string derivationScheme, int offset = 0,
int amount = 10,
CancellationToken token = default)
{
public virtual async Task<IEnumerable<OnChainPaymentMethodData>> GetStoreOnChainPaymentMethods(string storeId,
bool? enabled = null,
CancellationToken token = default)
{
var query = new Dictionary<string, object>();
if (enabled != null)
{
query.Add(nameof(enabled), enabled);
}
var response =
await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain",
query), token);
return await HandleResponse<IEnumerable<OnChainPaymentMethodData>>(response);
}
public virtual async Task<OnChainPaymentMethodData> GetStoreOnChainPaymentMethod(string storeId,
string cryptoCode, CancellationToken token = default)
{
var response =
await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}"), token);
return await HandleResponse<OnChainPaymentMethodData>(response);
}
public virtual async Task RemoveStoreOnChainPaymentMethod(string storeId,
string cryptoCode, CancellationToken token = default)
{
var response =
await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}",
method: HttpMethod.Delete), token);
await HandleResponse(response);
}
public virtual async Task<OnChainPaymentMethodData> UpdateStoreOnChainPaymentMethod(string storeId,
string cryptoCode, UpdateOnChainPaymentMethodRequest paymentMethod,
CancellationToken token = default)
{
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}",
bodyPayload: paymentMethod, method: HttpMethod.Put), token);
return await HandleResponse<OnChainPaymentMethodData>(response);
}
public virtual async Task<OnChainPaymentMethodPreviewResultData>
PreviewProposedStoreOnChainPaymentMethodAddresses(
string storeId, string cryptoCode, UpdateOnChainPaymentMethodRequest paymentMethod, int offset = 0,
int amount = 10,
CancellationToken token = default)
{
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/preview",
bodyPayload: paymentMethod,
queryPayload: new Dictionary<string, object>() { { "offset", offset }, { "amount", amount } },
method: HttpMethod.Post), token);
return await HandleResponse<OnChainPaymentMethodPreviewResultData>(response);
}
public virtual async Task<OnChainPaymentMethodPreviewResultData> PreviewStoreOnChainPaymentMethodAddresses(
string storeId, string cryptoCode, int offset = 0, int amount = 10,
CancellationToken token = default)
{
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/preview",
queryPayload: new Dictionary<string, object>() { { "offset", offset }, { "amount", amount } },
method: HttpMethod.Get), token);
return await HandleResponse<OnChainPaymentMethodPreviewResultData>(response);
}
public virtual async Task<OnChainPaymentMethodDataWithSensitiveData> GenerateOnChainWallet(string storeId,
string cryptoCode, GenerateOnChainWalletRequest request,
CancellationToken token = default)
{
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/generate",
bodyPayload: request,
method: HttpMethod.Post), token);
return await HandleResponse<OnChainPaymentMethodDataWithSensitiveData>(response);
}
return await SendHttpRequest<UpdatePaymentMethodRequest, OnChainPaymentMethodPreviewResultData>($"api/v1/stores/{storeId}/payment-methods/{paymentMethodId}/wallet/preview",
new Dictionary<string, object> { { "offset", offset }, { "amount", amount } },
new UpdatePaymentMethodRequest { Config = JValue.CreateString(derivationScheme) },
HttpMethod.Post, token);
}
public virtual async Task<OnChainPaymentMethodPreviewResultData> PreviewStoreOnChainPaymentMethodAddresses(
string storeId, string paymentMethodId, int offset = 0, int amount = 10,
CancellationToken token = default)
{
return await SendHttpRequest<OnChainPaymentMethodPreviewResultData>($"api/v1/stores/{storeId}/payment-methods/{paymentMethodId}/wallet/preview",
new Dictionary<string, object> { { "offset", offset }, { "amount", amount } }, HttpMethod.Get, token);
}
public virtual async Task<GenerateOnChainWalletResponse> GenerateOnChainWallet(string storeId,
string paymentMethodId, GenerateOnChainWalletRequest request,
CancellationToken token = default)
{
return await SendHttpRequest<GenerateOnChainWalletResponse>($"api/v1/stores/{storeId}/payment-methods/{paymentMethodId}/wallet/generate", request, HttpMethod.Post, token);
}
}

@ -1,82 +1,62 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Client.Models;
using NBitcoin;
namespace BTCPayServer.Client
namespace BTCPayServer.Client;
public partial class BTCPayServerClient
{
public partial class BTCPayServerClient
public virtual async Task<OnChainWalletObjectData> GetOnChainWalletObject(string storeId, string cryptoCode, OnChainWalletObjectId objectId, bool? includeNeighbourData = null, CancellationToken token = default)
{
public virtual async Task<OnChainWalletObjectData> GetOnChainWalletObject(string storeId, string cryptoCode, OnChainWalletObjectId objectId, bool? includeNeighbourData = null, CancellationToken token = default)
var parameters = new Dictionary<string, object>();
if (includeNeighbourData is bool v)
parameters.Add("includeNeighbourData", v);
try
{
Dictionary<string, object> parameters = new Dictionary<string, object>();
if (includeNeighbourData is bool v)
parameters.Add("includeNeighbourData", v);
var response =
await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/wallet/objects/{objectId.Type}/{objectId.Id}", parameters, method: HttpMethod.Get), token);
try
{
return await HandleResponse<OnChainWalletObjectData>(response);
}
catch (GreenfieldAPIException err) when (err.APIError.Code == "wallet-object-not-found")
{
return null;
}
return await SendHttpRequest<OnChainWalletObjectData>($"api/v1/stores/{storeId}/payment-methods/{cryptoCode}-CHAIN/wallet/objects/{objectId.Type}/{objectId.Id}", parameters, HttpMethod.Get, token);
}
public virtual async Task<OnChainWalletObjectData[]> GetOnChainWalletObjects(string storeId, string cryptoCode, GetWalletObjectsRequest query = null, CancellationToken token = default)
catch (GreenfieldAPIException err) when (err.APIError.Code == "wallet-object-not-found")
{
Dictionary<string, object> parameters = new Dictionary<string, object>();
if (query?.Type is string s)
parameters.Add("type", s);
if (query?.Ids is string[] ids)
parameters.Add("ids", ids);
if (query?.IncludeNeighbourData is bool v)
parameters.Add("includeNeighbourData", v);
var response =
await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/wallet/objects", parameters, method: HttpMethod.Get), token);
return await HandleResponse<OnChainWalletObjectData[]>(response);
}
public virtual async Task RemoveOnChainWalletObject(string storeId, string cryptoCode, OnChainWalletObjectId objectId,
CancellationToken token = default)
{
var response =
await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/wallet/objects/{objectId.Type}/{objectId.Id}", method: HttpMethod.Delete), token);
await HandleResponse(response);
}
public virtual async Task<OnChainWalletObjectData> AddOrUpdateOnChainWalletObject(string storeId, string cryptoCode, AddOnChainWalletObjectRequest request,
CancellationToken token = default)
{
var response =
await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/wallet/objects", method: HttpMethod.Post, bodyPayload: request), token);
return await HandleResponse<OnChainWalletObjectData>(response);
}
public virtual async Task AddOrUpdateOnChainWalletLink(string storeId, string cryptoCode,
OnChainWalletObjectId objectId,
AddOnChainWalletObjectLinkRequest request = null,
CancellationToken token = default)
{
var response =
await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/wallet/objects/{objectId.Type}/{objectId.Id}/links", method: HttpMethod.Post, bodyPayload: request), token);
await HandleResponse(response);
}
public virtual async Task RemoveOnChainWalletLinks(string storeId, string cryptoCode,
OnChainWalletObjectId objectId,
OnChainWalletObjectId link,
CancellationToken token = default)
{
var response =
await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/wallet/objects/{objectId.Type}/{objectId.Id}/links/{link.Type}/{link.Id}", method: HttpMethod.Delete), token);
await HandleResponse(response);
return null;
}
}
public virtual async Task<OnChainWalletObjectData[]> GetOnChainWalletObjects(string storeId, string cryptoCode, GetWalletObjectsRequest query = null, CancellationToken token = default)
{
Dictionary<string, object> parameters = new Dictionary<string, object>();
if (query?.Type is string s)
parameters.Add("type", s);
if (query?.Ids is string[] ids)
parameters.Add("ids", ids);
if (query?.IncludeNeighbourData is bool v)
parameters.Add("includeNeighbourData", v);
return await SendHttpRequest<OnChainWalletObjectData[]>($"api/v1/stores/{storeId}/payment-methods/{cryptoCode}-CHAIN/wallet/objects", parameters, HttpMethod.Get, token);
}
public virtual async Task RemoveOnChainWalletObject(string storeId, string cryptoCode, OnChainWalletObjectId objectId,
CancellationToken token = default)
{
await SendHttpRequest($"api/v1/stores/{storeId}/payment-methods/{cryptoCode}-CHAIN/wallet/objects/{objectId.Type}/{objectId.Id}", null, HttpMethod.Delete, token);
}
public virtual async Task<OnChainWalletObjectData> AddOrUpdateOnChainWalletObject(string storeId, string cryptoCode, AddOnChainWalletObjectRequest request,
CancellationToken token = default)
{
return await SendHttpRequest<OnChainWalletObjectData>($"api/v1/stores/{storeId}/payment-methods/{cryptoCode}-CHAIN/wallet/objects", request, HttpMethod.Post, token);
}
public virtual async Task AddOrUpdateOnChainWalletLink(string storeId, string cryptoCode,
OnChainWalletObjectId objectId,
AddOnChainWalletObjectLinkRequest request = null,
CancellationToken token = default)
{
await SendHttpRequest($"api/v1/stores/{storeId}/payment-methods/{cryptoCode}-CHAIN/wallet/objects/{objectId.Type}/{objectId.Id}/links", request, HttpMethod.Post, token);
}
public virtual async Task RemoveOnChainWalletLinks(string storeId, string cryptoCode,
OnChainWalletObjectId objectId,
OnChainWalletObjectId link,
CancellationToken token = default)
{
await SendHttpRequest($"api/v1/stores/{storeId}/payment-methods/{cryptoCode}-CHAIN/wallet/objects/{objectId.Type}/{objectId.Id}/links/{link.Type}/{link.Id}", null, HttpMethod.Delete, token);
}
}

@ -7,138 +7,105 @@ using System.Threading.Tasks;
using BTCPayServer.Client.Models;
using NBitcoin;
namespace BTCPayServer.Client
namespace BTCPayServer.Client;
public partial class BTCPayServerClient
{
public partial class BTCPayServerClient
public virtual async Task<OnChainWalletOverviewData> ShowOnChainWalletOverview(string storeId, string cryptoCode,
CancellationToken token = default)
{
public virtual async Task<OnChainWalletOverviewData> ShowOnChainWalletOverview(string storeId, string cryptoCode,
CancellationToken token = default)
return await SendHttpRequest<OnChainWalletOverviewData>($"api/v1/stores/{storeId}/payment-methods/{cryptoCode}-CHAIN/wallet", null, HttpMethod.Get, token);
}
public virtual async Task<OnChainWalletFeeRateData> GetOnChainFeeRate(string storeId, string cryptoCode, int? blockTarget = null,
CancellationToken token = default)
{
var queryParams = new Dictionary<string, object>();
if (blockTarget != null)
{
var response =
await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/wallet"), token);
return await HandleResponse<OnChainWalletOverviewData>(response);
}
public virtual async Task<OnChainWalletFeeRateData> GetOnChainFeeRate(string storeId, string cryptoCode, int? blockTarget = null,
CancellationToken token = default)
{
Dictionary<string, object> queryParams = new Dictionary<string, object>();
if (blockTarget != null)
{
queryParams.Add("blockTarget", blockTarget);
}
var response =
await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/wallet/feeRate", queryParams), token);
return await HandleResponse<OnChainWalletFeeRateData>(response);
queryParams.Add("blockTarget", blockTarget);
}
return await SendHttpRequest<OnChainWalletFeeRateData>($"api/v1/stores/{storeId}/payment-methods/{cryptoCode}-CHAIN/wallet/feerate", queryParams, HttpMethod.Get, token);
}
public virtual async Task<OnChainWalletAddressData> GetOnChainWalletReceiveAddress(string storeId, string cryptoCode, bool forceGenerate = false,
CancellationToken token = default)
public virtual async Task<OnChainWalletAddressData> GetOnChainWalletReceiveAddress(string storeId, string cryptoCode, bool forceGenerate = false,
CancellationToken token = default)
{
return await SendHttpRequest<OnChainWalletAddressData>($"api/v1/stores/{storeId}/payment-methods/{cryptoCode}-CHAIN/wallet/address", new Dictionary<string, object>
{
var response =
await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/wallet/address", new Dictionary<string, object>()
{
{"forceGenerate", forceGenerate}
}), token);
return await HandleResponse<OnChainWalletAddressData>(response);
}
{"forceGenerate", forceGenerate}
}, HttpMethod.Get, token);
}
public virtual async Task UnReserveOnChainWalletReceiveAddress(string storeId, string cryptoCode,
CancellationToken token = default)
{
var response =
await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/wallet/address", method: HttpMethod.Delete), token);
await HandleResponse(response);
}
public virtual async Task UnReserveOnChainWalletReceiveAddress(string storeId, string cryptoCode,
CancellationToken token = default)
{
await SendHttpRequest($"api/v1/stores/{storeId}/payment-methods/{cryptoCode}-CHAIN/wallet/address", null, HttpMethod.Delete, token);
}
public virtual async Task<IEnumerable<OnChainWalletTransactionData>> ShowOnChainWalletTransactions(
string storeId, string cryptoCode, TransactionStatus[] statusFilter = null, string labelFilter = null, int skip = 0,
CancellationToken token = default)
public virtual async Task<IEnumerable<OnChainWalletTransactionData>> ShowOnChainWalletTransactions(
string storeId, string cryptoCode, TransactionStatus[] statusFilter = null, string labelFilter = null, int skip = 0,
CancellationToken token = default)
{
var query = new Dictionary<string, object>();
if (statusFilter?.Any() is true)
{
var query = new Dictionary<string, object>();
if (statusFilter?.Any() is true)
{
query.Add(nameof(statusFilter), statusFilter);
}
if (labelFilter != null)
{
query.Add(nameof(labelFilter), labelFilter);
}
if (skip != 0)
{
query.Add(nameof(skip), skip);
}
var response =
await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/wallet/transactions", query), token);
return await HandleResponse<IEnumerable<OnChainWalletTransactionData>>(response);
query.Add(nameof(statusFilter), statusFilter);
}
if (labelFilter != null)
{
query.Add(nameof(labelFilter), labelFilter);
}
if (skip != 0)
{
query.Add(nameof(skip), skip);
}
return await SendHttpRequest<IEnumerable<OnChainWalletTransactionData>>($"api/v1/stores/{storeId}/payment-methods/{cryptoCode}-CHAIN/wallet/transactions", query, HttpMethod.Get, token);
}
public virtual async Task<OnChainWalletTransactionData> GetOnChainWalletTransaction(
string storeId, string cryptoCode, string transactionId,
CancellationToken token = default)
{
var response =
await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/wallet/transactions/{transactionId}"), token);
return await HandleResponse<OnChainWalletTransactionData>(response);
}
public virtual async Task<OnChainWalletTransactionData> GetOnChainWalletTransaction(
string storeId, string cryptoCode, string transactionId,
CancellationToken token = default)
{
return await SendHttpRequest<OnChainWalletTransactionData>($"api/v1/stores/{storeId}/payment-methods/{cryptoCode}-CHAIN/wallet/transactions/{transactionId}", null, HttpMethod.Get, token);
}
public virtual async Task<OnChainWalletTransactionData> PatchOnChainWalletTransaction(
string storeId, string cryptoCode, string transactionId,
PatchOnChainTransactionRequest request,
bool force = false, CancellationToken token = default)
{
var response =
await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/wallet/transactions/{transactionId}", queryPayload: new Dictionary<string, object>()
{
{"force", force}
}, bodyPayload: request, HttpMethod.Patch), token);
return await HandleResponse<OnChainWalletTransactionData>(response);
}
public virtual async Task<OnChainWalletTransactionData> PatchOnChainWalletTransaction(
string storeId, string cryptoCode, string transactionId,
PatchOnChainTransactionRequest request,
bool force = false, CancellationToken token = default)
{
return await SendHttpRequest<PatchOnChainTransactionRequest, OnChainWalletTransactionData>($"api/v1/stores/{storeId}/payment-methods/{cryptoCode}-CHAIN/wallet/transactions/{transactionId}",
new Dictionary<string, object> { {"force", force} }, request, HttpMethod.Patch, token);
}
public virtual async Task<IEnumerable<OnChainWalletUTXOData>> GetOnChainWalletUTXOs(string storeId,
string cryptoCode,
CancellationToken token = default)
{
var response =
await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/wallet/utxos"), token);
return await HandleResponse<IEnumerable<OnChainWalletUTXOData>>(response);
}
public virtual async Task<IEnumerable<OnChainWalletUTXOData>> GetOnChainWalletUTXOs(string storeId,
string cryptoCode,
CancellationToken token = default)
{
return await SendHttpRequest<IEnumerable<OnChainWalletUTXOData>>($"api/v1/stores/{storeId}/payment-methods/{cryptoCode}-CHAIN/wallet/utxos", null, HttpMethod.Get, token);
}
public virtual async Task<OnChainWalletTransactionData> CreateOnChainTransaction(string storeId,
string cryptoCode, CreateOnChainTransactionRequest request,
CancellationToken token = default)
public virtual async Task<OnChainWalletTransactionData> CreateOnChainTransaction(string storeId,
string cryptoCode, CreateOnChainTransactionRequest request,
CancellationToken token = default)
{
if (!request.ProceedWithBroadcast)
{
if (!request.ProceedWithBroadcast)
{
throw new ArgumentOutOfRangeException(nameof(request.ProceedWithBroadcast),
"Please use CreateOnChainTransactionButDoNotBroadcast when wanting to only create the transaction");
}
var response =
await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/wallet/transactions", null, request, HttpMethod.Post), token);
return await HandleResponse<OnChainWalletTransactionData>(response);
throw new ArgumentOutOfRangeException(nameof(request.ProceedWithBroadcast),
"Please use CreateOnChainTransactionButDoNotBroadcast when wanting to only create the transaction");
}
return await SendHttpRequest<OnChainWalletTransactionData>($"api/v1/stores/{storeId}/payment-methods/{cryptoCode}-CHAIN/wallet/transactions", request, HttpMethod.Post, token);
}
public virtual async Task<Transaction> CreateOnChainTransactionButDoNotBroadcast(string storeId,
string cryptoCode, CreateOnChainTransactionRequest request, Network network,
CancellationToken token = default)
public virtual async Task<Transaction> CreateOnChainTransactionButDoNotBroadcast(string storeId,
string cryptoCode, CreateOnChainTransactionRequest request, Network network,
CancellationToken token = default)
{
if (request.ProceedWithBroadcast)
{
if (request.ProceedWithBroadcast)
{
throw new ArgumentOutOfRangeException(nameof(request.ProceedWithBroadcast),
"Please use CreateOnChainTransaction when wanting to also broadcast the transaction");
}
var response =
await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/wallet/transactions", null, request, HttpMethod.Post), token);
return Transaction.Parse(await HandleResponse<string>(response), network);
throw new ArgumentOutOfRangeException(nameof(request.ProceedWithBroadcast),
"Please use CreateOnChainTransaction when wanting to also broadcast the transaction");
}
return Transaction.Parse(await SendHttpRequest<string>($"api/v1/stores/{storeId}/payment-methods/{cryptoCode}-CHAIN/wallet/transactions", request, HttpMethod.Post, token), network);
}
}

@ -5,72 +5,49 @@ using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Client.Models;
namespace BTCPayServer.Client
namespace BTCPayServer.Client;
public partial class BTCPayServerClient
{
public partial class BTCPayServerClient
public virtual async Task<IEnumerable<PaymentRequestData>> GetPaymentRequests(string storeId,
bool includeArchived = false,
CancellationToken token = default)
{
public virtual async Task<IEnumerable<PaymentRequestData>> GetPaymentRequests(string storeId,
bool includeArchived = false,
CancellationToken token = default)
{
var response =
await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/payment-requests",
new Dictionary<string, object>() { { nameof(includeArchived), includeArchived } }), token);
return await HandleResponse<IEnumerable<PaymentRequestData>>(response);
}
return await SendHttpRequest<IEnumerable<PaymentRequestData>>($"api/v1/stores/{storeId}/payment-requests",
new Dictionary<string, object> { { nameof(includeArchived), includeArchived } }, HttpMethod.Get, token);
}
public virtual async Task<PaymentRequestData> GetPaymentRequest(string storeId, string paymentRequestId,
CancellationToken token = default)
{
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/payment-requests/{paymentRequestId}"), token);
return await HandleResponse<PaymentRequestData>(response);
}
public virtual async Task<PaymentRequestData> GetPaymentRequest(string storeId, string paymentRequestId,
CancellationToken token = default)
{
return await SendHttpRequest<PaymentRequestData>($"api/v1/stores/{storeId}/payment-requests/{paymentRequestId}", null, HttpMethod.Get, token);
}
public virtual async Task ArchivePaymentRequest(string storeId, string paymentRequestId,
CancellationToken token = default)
{
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/payment-requests/{paymentRequestId}",
method: HttpMethod.Delete), token);
await HandleResponse(response);
}
public virtual async Task ArchivePaymentRequest(string storeId, string paymentRequestId,
CancellationToken token = default)
{
await SendHttpRequest($"api/v1/stores/{storeId}/payment-requests/{paymentRequestId}", null, HttpMethod.Delete, token);
}
public virtual async Task<Client.Models.InvoiceData> PayPaymentRequest(string storeId, string paymentRequestId, PayPaymentRequestRequest request, CancellationToken token = default)
{
if (request == null)
throw new ArgumentNullException(nameof(request));
if (storeId is null)
throw new ArgumentNullException(nameof(storeId));
if (paymentRequestId is null)
throw new ArgumentNullException(nameof(paymentRequestId));
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/payment-requests/{paymentRequestId}/pay", bodyPayload: request,
method: HttpMethod.Post), token);
return await HandleResponse<Client.Models.InvoiceData>(response);
}
public virtual async Task<Client.Models.InvoiceData> PayPaymentRequest(string storeId, string paymentRequestId, PayPaymentRequestRequest request, CancellationToken token = default)
{
if (request == null) throw new ArgumentNullException(nameof(request));
if (storeId is null) throw new ArgumentNullException(nameof(storeId));
if (paymentRequestId is null) throw new ArgumentNullException(nameof(paymentRequestId));
return await SendHttpRequest<InvoiceData>($"api/v1/stores/{storeId}/payment-requests/{paymentRequestId}/pay", request, HttpMethod.Post, token);
}
public virtual async Task<PaymentRequestData> CreatePaymentRequest(string storeId,
CreatePaymentRequestRequest request, CancellationToken token = default)
{
if (request == null)
throw new ArgumentNullException(nameof(request));
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/payment-requests", bodyPayload: request,
method: HttpMethod.Post), token);
return await HandleResponse<PaymentRequestData>(response);
}
public virtual async Task<PaymentRequestData> CreatePaymentRequest(string storeId,
CreatePaymentRequestRequest request, CancellationToken token = default)
{
if (request == null) throw new ArgumentNullException(nameof(request));
return await SendHttpRequest<PaymentRequestData>($"api/v1/stores/{storeId}/payment-requests", request, HttpMethod.Post, token);
}
public virtual async Task<PaymentRequestData> UpdatePaymentRequest(string storeId, string paymentRequestId,
UpdatePaymentRequestRequest request, CancellationToken token = default)
{
if (request == null)
throw new ArgumentNullException(nameof(request));
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/payment-requests/{paymentRequestId}", bodyPayload: request,
method: HttpMethod.Put), token);
return await HandleResponse<PaymentRequestData>(response);
}
public virtual async Task<PaymentRequestData> UpdatePaymentRequest(string storeId, string paymentRequestId,
UpdatePaymentRequestRequest request, CancellationToken token = default)
{
if (request == null) throw new ArgumentNullException(nameof(request));
return await SendHttpRequest<PaymentRequestData>($"api/v1/stores/{storeId}/payment-requests/{paymentRequestId}", request, HttpMethod.Put, token);
}
}

@ -1,18 +1,16 @@
#nullable enable
using System.Collections.Generic;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Client.Models;
namespace BTCPayServer.Client
namespace BTCPayServer.Client;
public partial class BTCPayServerClient
{
public partial class BTCPayServerClient
public virtual async Task<IEnumerable<PayoutProcessorData>> GetPayoutProcessors(CancellationToken token = default)
{
public virtual async Task<IEnumerable<PayoutProcessorData>> GetPayoutProcessors(
CancellationToken token = default)
{
var response = await _httpClient.SendAsync(CreateHttpRequest("api/v1/payout-processors"), token);
return await HandleResponse<IEnumerable<PayoutProcessorData>>(response);
}
return await SendHttpRequest<IEnumerable<PayoutProcessorData>>("api/v1/payout-processors", null, HttpMethod.Get, token);
}
}

@ -5,113 +5,90 @@ using System.Threading.Tasks;
using System.Web;
using BTCPayServer.Client.Models;
namespace BTCPayServer.Client
namespace BTCPayServer.Client;
public partial class BTCPayServerClient
{
public partial class BTCPayServerClient
public virtual async Task<PullPaymentData> CreatePullPayment(string storeId, CreatePullPaymentRequest request, CancellationToken cancellationToken = default)
{
public virtual async Task<PullPaymentData> CreatePullPayment(string storeId, CreatePullPaymentRequest request, CancellationToken cancellationToken = default)
{
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{HttpUtility.UrlEncode(storeId)}/pull-payments", bodyPayload: request, method: HttpMethod.Post), cancellationToken);
return await HandleResponse<PullPaymentData>(response);
}
public virtual async Task<PullPaymentData> GetPullPayment(string pullPaymentId, CancellationToken cancellationToken = default)
{
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/pull-payments/{HttpUtility.UrlEncode(pullPaymentId)}", method: HttpMethod.Get), cancellationToken);
return await HandleResponse<PullPaymentData>(response);
}
return await SendHttpRequest<PullPaymentData>($"api/v1/stores/{HttpUtility.UrlEncode(storeId)}/pull-payments", request, HttpMethod.Post, cancellationToken);
}
public virtual async Task<RegisterBoltcardResponse> RegisterBoltcard(string pullPaymentId, RegisterBoltcardRequest request, CancellationToken cancellationToken = default)
{
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/pull-payments/{HttpUtility.UrlEncode(pullPaymentId)}/boltcards", bodyPayload: request, method: HttpMethod.Post), cancellationToken);
return await HandleResponse<RegisterBoltcardResponse>(response);
}
public virtual async Task<PullPaymentData> GetPullPayment(string pullPaymentId, CancellationToken cancellationToken = default)
{
return await SendHttpRequest<PullPaymentData>($"api/v1/pull-payments/{HttpUtility.UrlEncode(pullPaymentId)}", null, HttpMethod.Get, cancellationToken);
}
public virtual async Task<PullPaymentData[]> GetPullPayments(string storeId, bool includeArchived = false, CancellationToken cancellationToken = default)
{
Dictionary<string, object> query = new Dictionary<string, object>();
query.Add("includeArchived", includeArchived);
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{HttpUtility.UrlEncode(storeId)}/pull-payments", queryPayload: query, method: HttpMethod.Get), cancellationToken);
return await HandleResponse<PullPaymentData[]>(response);
}
public virtual async Task<RegisterBoltcardResponse> RegisterBoltcard(string pullPaymentId, RegisterBoltcardRequest request, CancellationToken cancellationToken = default)
{
return await SendHttpRequest<RegisterBoltcardResponse>($"api/v1/pull-payments/{HttpUtility.UrlEncode(pullPaymentId)}/boltcards", request, HttpMethod.Post, cancellationToken);
}
public virtual async Task ArchivePullPayment(string storeId, string pullPaymentId, CancellationToken cancellationToken = default)
{
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{HttpUtility.UrlEncode(storeId)}/pull-payments/{HttpUtility.UrlEncode(pullPaymentId)}", method: HttpMethod.Delete), cancellationToken);
await HandleResponse(response);
}
public virtual async Task<PullPaymentData[]> GetPullPayments(string storeId, bool includeArchived = false, CancellationToken cancellationToken = default)
{
var query = new Dictionary<string, object> { { "includeArchived", includeArchived } };
return await SendHttpRequest<PullPaymentData[]>($"api/v1/stores/{HttpUtility.UrlEncode(storeId)}/pull-payments", query, HttpMethod.Get, cancellationToken);
}
public virtual async Task<PayoutData[]> GetPayouts(string pullPaymentId, bool includeCancelled = false, CancellationToken cancellationToken = default)
{
Dictionary<string, object> query = new Dictionary<string, object>();
query.Add("includeCancelled", includeCancelled);
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/pull-payments/{HttpUtility.UrlEncode(pullPaymentId)}/payouts", queryPayload: query, method: HttpMethod.Get), cancellationToken);
return await HandleResponse<PayoutData[]>(response);
}
public virtual async Task<PayoutData[]> GetStorePayouts(string storeId, bool includeCancelled = false, CancellationToken cancellationToken = default)
{
Dictionary<string, object> query = new Dictionary<string, object>();
query.Add("includeCancelled", includeCancelled);
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/payouts", queryPayload: query, method: HttpMethod.Get), cancellationToken);
return await HandleResponse<PayoutData[]>(response);
}
public virtual async Task<PayoutData> CreatePayout(string pullPaymentId, CreatePayoutRequest payoutRequest, CancellationToken cancellationToken = default)
{
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/pull-payments/{HttpUtility.UrlEncode(pullPaymentId)}/payouts", bodyPayload: payoutRequest, method: HttpMethod.Post), cancellationToken);
return await HandleResponse<PayoutData>(response);
}
public virtual async Task<PayoutData> GetPullPaymentPayout(string pullPaymentId, string payoutId, CancellationToken cancellationToken = default)
{
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/pull-payments/{HttpUtility.UrlEncode(pullPaymentId)}/payouts/{payoutId}", method: HttpMethod.Get), cancellationToken);
return await HandleResponse<PayoutData>(response);
}
public virtual async Task<PayoutData> GetStorePayout(string storeId, string payoutId, CancellationToken cancellationToken = default)
{
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/payouts/{payoutId}", method: HttpMethod.Get), cancellationToken);
return await HandleResponse<PayoutData>(response);
}
public virtual async Task<PayoutData> CreatePayout(string storeId, CreatePayoutThroughStoreRequest payoutRequest, CancellationToken cancellationToken = default)
{
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/payouts", bodyPayload: payoutRequest, method: HttpMethod.Post), cancellationToken);
return await HandleResponse<PayoutData>(response);
}
public virtual async Task CancelPayout(string storeId, string payoutId, CancellationToken cancellationToken = default)
{
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{HttpUtility.UrlEncode(storeId)}/payouts/{HttpUtility.UrlEncode(payoutId)}", method: HttpMethod.Delete), cancellationToken);
await HandleResponse(response);
}
public virtual async Task<PayoutData> ApprovePayout(string storeId, string payoutId, ApprovePayoutRequest request, CancellationToken cancellationToken = default)
{
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{HttpUtility.UrlEncode(storeId)}/payouts/{HttpUtility.UrlEncode(payoutId)}", bodyPayload: request, method: HttpMethod.Post), cancellationToken);
return await HandleResponse<PayoutData>(response);
}
public virtual async Task ArchivePullPayment(string storeId, string pullPaymentId, CancellationToken cancellationToken = default)
{
await SendHttpRequest($"api/v1/stores/{HttpUtility.UrlEncode(storeId)}/pull-payments/{HttpUtility.UrlEncode(pullPaymentId)}", null, HttpMethod.Delete, cancellationToken);
}
public virtual async Task MarkPayoutPaid(string storeId, string payoutId,
CancellationToken cancellationToken = default)
{
var response = await _httpClient.SendAsync(
CreateHttpRequest(
$"api/v1/stores/{HttpUtility.UrlEncode(storeId)}/payouts/{HttpUtility.UrlEncode(payoutId)}/mark-paid",
method: HttpMethod.Post), cancellationToken);
await HandleResponse(response);
}
public virtual async Task MarkPayout(string storeId, string payoutId, MarkPayoutRequest request,
CancellationToken cancellationToken = default)
{
var response = await _httpClient.SendAsync(
CreateHttpRequest(
$"api/v1/stores/{HttpUtility.UrlEncode(storeId)}/payouts/{HttpUtility.UrlEncode(payoutId)}/mark",
method: HttpMethod.Post, bodyPayload: request), cancellationToken);
await HandleResponse(response);
}
public virtual async Task<PayoutData[]> GetPayouts(string pullPaymentId, bool includeCancelled = false, CancellationToken cancellationToken = default)
{
var query = new Dictionary<string, object> { { "includeCancelled", includeCancelled } };
return await SendHttpRequest<PayoutData[]>($"api/v1/pull-payments/{HttpUtility.UrlEncode(pullPaymentId)}/payouts", query, HttpMethod.Get, cancellationToken);
}
public virtual async Task<PullPaymentLNURL> GetPullPaymentLNURL(string pullPaymentId,
CancellationToken cancellationToken = default)
{
var response = await _httpClient.SendAsync(
CreateHttpRequest(
$"/api/v1/pull-payments/{pullPaymentId}/lnurl",
method: HttpMethod.Get), cancellationToken);
return await HandleResponse<PullPaymentLNURL>(response);
}
public virtual async Task<PayoutData[]> GetStorePayouts(string storeId, bool includeCancelled = false, CancellationToken cancellationToken = default)
{
var query = new Dictionary<string, object> { { "includeCancelled", includeCancelled } };
return await SendHttpRequest<PayoutData[]>($"api/v1/stores/{storeId}/payouts", queryPayload: query, method: HttpMethod.Get, cancellationToken);
}
public virtual async Task<PayoutData> CreatePayout(string pullPaymentId, CreatePayoutRequest payoutRequest, CancellationToken cancellationToken = default)
{
return await SendHttpRequest<PayoutData>($"api/v1/pull-payments/{HttpUtility.UrlEncode(pullPaymentId)}/payouts", bodyPayload: payoutRequest, HttpMethod.Post, cancellationToken);
}
public virtual async Task<PayoutData> GetPullPaymentPayout(string pullPaymentId, string payoutId, CancellationToken cancellationToken = default)
{
return await SendHttpRequest<PayoutData>($"api/v1/pull-payments/{HttpUtility.UrlEncode(pullPaymentId)}/payouts/{payoutId}", null, HttpMethod.Get, cancellationToken);
}
public virtual async Task<PayoutData> GetStorePayout(string storeId, string payoutId, CancellationToken cancellationToken = default)
{
return await SendHttpRequest<PayoutData>($"api/v1/stores/{storeId}/payouts/{payoutId}", null, HttpMethod.Get, cancellationToken);
}
public virtual async Task<PayoutData> CreatePayout(string storeId, CreatePayoutThroughStoreRequest payoutRequest, CancellationToken cancellationToken = default)
{
return await SendHttpRequest<PayoutData>($"api/v1/stores/{storeId}/payouts", bodyPayload: payoutRequest, method: HttpMethod.Post, cancellationToken);
}
public virtual async Task CancelPayout(string storeId, string payoutId, CancellationToken cancellationToken = default)
{
await SendHttpRequest($"api/v1/stores/{HttpUtility.UrlEncode(storeId)}/payouts/{HttpUtility.UrlEncode(payoutId)}", null, HttpMethod.Delete, cancellationToken);
}
public virtual async Task<PayoutData> ApprovePayout(string storeId, string payoutId, ApprovePayoutRequest request, CancellationToken cancellationToken = default)
{
return await SendHttpRequest<PayoutData>($"api/v1/stores/{HttpUtility.UrlEncode(storeId)}/payouts/{HttpUtility.UrlEncode(payoutId)}", request, HttpMethod.Post, cancellationToken);
}
public virtual async Task MarkPayoutPaid(string storeId, string payoutId, CancellationToken cancellationToken = default)
{
await SendHttpRequest($"api/v1/stores/{HttpUtility.UrlEncode(storeId)}/payouts/{HttpUtility.UrlEncode(payoutId)}/mark-paid", null, HttpMethod.Post, cancellationToken);
}
public virtual async Task MarkPayout(string storeId, string payoutId, MarkPayoutRequest request, CancellationToken cancellationToken = default)
{
await SendHttpRequest($"api/v1/stores/{HttpUtility.UrlEncode(storeId)}/payouts/{HttpUtility.UrlEncode(payoutId)}/mark", request, HttpMethod.Post, cancellationToken);
}
public virtual async Task<PullPaymentLNURL> GetPullPaymentLNURL(string pullPaymentId, CancellationToken cancellationToken = default)
{
return await SendHttpRequest<PullPaymentLNURL>($"api/v1/pull-payments/{HttpUtility.UrlEncode(pullPaymentId)}/lnurl", null, HttpMethod.Get, cancellationToken);
}
}

@ -1,22 +1,20 @@
using System.Collections.Generic;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Client.Models;
namespace BTCPayServer.Client
namespace BTCPayServer.Client;
public partial class BTCPayServerClient
{
public partial class BTCPayServerClient
public virtual async Task<ServerInfoData> GetServerInfo(CancellationToken token = default)
{
public virtual async Task<ServerInfoData> GetServerInfo(CancellationToken token = default)
{
var response = await _httpClient.SendAsync(CreateHttpRequest("api/v1/server/info"), token);
return await HandleResponse<ServerInfoData>(response);
}
return await SendHttpRequest<ServerInfoData>("api/v1/server/info", null, HttpMethod.Get, token);
}
public virtual async Task<List<RoleData>> GetServerRoles(CancellationToken token = default)
{
using var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/server/roles"), token);
return await HandleResponse<List<RoleData>>(response);
}
public virtual async Task<List<RoleData>> GetServerRoles(CancellationToken token = default)
{
return await SendHttpRequest<List<RoleData>>("api/v1/server/roles", null, HttpMethod.Get, token);
}
}

@ -3,35 +3,22 @@ using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Client.Models;
namespace BTCPayServer.Client
namespace BTCPayServer.Client;
public partial class BTCPayServerClient
{
public partial class BTCPayServerClient
public virtual async Task<EmailSettingsData> GetStoreEmailSettings(string storeId, CancellationToken token = default)
{
public virtual async Task<EmailSettingsData> GetStoreEmailSettings(string storeId,
CancellationToken token = default)
{
using var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/email", method: HttpMethod.Get),
token);
return await HandleResponse<EmailSettingsData>(response);
}
return await SendHttpRequest<EmailSettingsData>($"api/v1/stores/{storeId}/email", null, HttpMethod.Get, token);
}
public virtual async Task<EmailSettingsData> UpdateStoreEmailSettings(string storeId, EmailSettingsData request,
CancellationToken token = default)
{
using var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/email", bodyPayload: request, method: HttpMethod.Put),
token);
return await HandleResponse<EmailSettingsData>(response);
}
public virtual async Task<EmailSettingsData> UpdateStoreEmailSettings(string storeId, EmailSettingsData request, CancellationToken token = default)
{
return await SendHttpRequest<EmailSettingsData>($"api/v1/stores/{storeId}/email", request, method: HttpMethod.Put, token);
}
public virtual async Task SendEmail(string storeId, SendEmailRequest request,
CancellationToken token = default)
{
using var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/email/send", bodyPayload: request, method: HttpMethod.Post),
token);
await HandleResponse(response);
}
public virtual async Task SendEmail(string storeId, SendEmailRequest request, CancellationToken token = default)
{
await SendHttpRequest($"api/v1/stores/{storeId}/email/send", request, HttpMethod.Post, token);
}
}

@ -1,27 +1,45 @@
using System.Collections.Generic;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Client.Models;
namespace BTCPayServer.Client
{
public partial class BTCPayServerClient
{
public virtual async Task<Dictionary<string, GenericPaymentMethodData>> GetStorePaymentMethods(string storeId,
bool? enabled = null,
CancellationToken token = default)
{
var query = new Dictionary<string, object>();
if (enabled != null)
{
query.Add(nameof(enabled), enabled);
}
namespace BTCPayServer.Client;
var response =
await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods",
query), token);
return await HandleResponse<Dictionary<string, GenericPaymentMethodData>>(response);
public partial class BTCPayServerClient
{
public virtual async Task<GenericPaymentMethodData> UpdateStorePaymentMethod(string storeId, string paymentMethodId, UpdatePaymentMethodRequest request, CancellationToken token = default)
{
return await SendHttpRequest<GenericPaymentMethodData>($"api/v1/stores/{storeId}/payment-methods/{paymentMethodId}", request, HttpMethod.Put, token);
}
public virtual async Task RemoveStorePaymentMethod(string storeId, string paymentMethodId)
{
await SendHttpRequest($"api/v1/stores/{storeId}/payment-methods/{paymentMethodId}", null, HttpMethod.Delete, CancellationToken.None);
}
public virtual async Task<GenericPaymentMethodData> GetStorePaymentMethod(string storeId, string paymentMethodId, bool? includeConfig = null, CancellationToken token = default)
{
var query = new Dictionary<string, object>();
if (includeConfig != null)
{
query.Add(nameof(includeConfig), includeConfig);
}
return await SendHttpRequest<GenericPaymentMethodData>($"api/v1/stores/{storeId}/payment-methods/{paymentMethodId}", query, HttpMethod.Get, token);
}
public virtual async Task<GenericPaymentMethodData[]> GetStorePaymentMethods(string storeId, bool? onlyEnabled = null, bool? includeConfig = null, CancellationToken token = default)
{
var query = new Dictionary<string, object>();
if (onlyEnabled != null)
{
query.Add(nameof(onlyEnabled), onlyEnabled);
}
if (includeConfig != null)
{
query.Add(nameof(includeConfig), includeConfig);
}
return await SendHttpRequest<GenericPaymentMethodData[]>($"api/v1/stores/{storeId}/payment-methods", query, HttpMethod.Get, token);
}
}

@ -5,44 +5,37 @@ using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Client.Models;
namespace BTCPayServer.Client
namespace BTCPayServer.Client;
public partial class BTCPayServerClient
{
public partial class BTCPayServerClient
public virtual async Task<IEnumerable<PayoutProcessorData>> GetPayoutProcessors(string storeId, CancellationToken token = default)
{
public virtual async Task<IEnumerable<PayoutProcessorData>> GetPayoutProcessors(string storeId,
CancellationToken token = default)
{
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/payout-processors"), token);
return await HandleResponse<IEnumerable<PayoutProcessorData>>(response);
}
public virtual async Task RemovePayoutProcessor(string storeId, string processor, string paymentMethod, CancellationToken token = default)
{
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/payout-processors/{processor}/{paymentMethod}", null, HttpMethod.Delete), token);
await HandleResponse(response);
}
return await SendHttpRequest<IEnumerable<PayoutProcessorData>>($"api/v1/stores/{storeId}/payout-processors", null, HttpMethod.Get, token);
}
public virtual async Task<IEnumerable<LightningAutomatedPayoutSettings>> GetStoreLightningAutomatedPayoutProcessors(string storeId, string? paymentMethod = null,
CancellationToken token = default)
{
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/payout-processors/LightningAutomatedPayoutSenderFactory{(paymentMethod is null ? string.Empty : $"/{paymentMethod}")}"), token);
return await HandleResponse<IEnumerable<LightningAutomatedPayoutSettings>>(response);
}
public virtual async Task<LightningAutomatedPayoutSettings> UpdateStoreLightningAutomatedPayoutProcessors(string storeId, string paymentMethod, LightningAutomatedPayoutSettings request, CancellationToken token = default)
{
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/payout-processors/LightningAutomatedPayoutSenderFactory/{paymentMethod}", null, request, HttpMethod.Put), token);
return await HandleResponse<LightningAutomatedPayoutSettings>(response);
}
public virtual async Task<OnChainAutomatedPayoutSettings> UpdateStoreOnChainAutomatedPayoutProcessors(string storeId, string paymentMethod, OnChainAutomatedPayoutSettings request, CancellationToken token = default)
{
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/payout-processors/OnChainAutomatedPayoutSenderFactory/{paymentMethod}", null, request, HttpMethod.Put), token);
return await HandleResponse<OnChainAutomatedPayoutSettings>(response);
}
public virtual async Task RemovePayoutProcessor(string storeId, string processor, string paymentMethod, CancellationToken token = default)
{
await SendHttpRequest($"api/v1/stores/{storeId}/payout-processors/{processor}/{paymentMethod}", null, HttpMethod.Delete, token);
}
public virtual async Task<IEnumerable<OnChainAutomatedPayoutSettings>> GetStoreOnChainAutomatedPayoutProcessors(string storeId, string? paymentMethod = null,
CancellationToken token = default)
{
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/payout-processors/OnChainAutomatedPayoutSenderFactory{(paymentMethod is null ? string.Empty : $"/{paymentMethod}")}"), token);
return await HandleResponse<IEnumerable<OnChainAutomatedPayoutSettings>>(response);
}
public virtual async Task<IEnumerable<LightningAutomatedPayoutSettings>> GetStoreLightningAutomatedPayoutProcessors(string storeId, string? payoutMethodId = null, CancellationToken token = default)
{
return await SendHttpRequest<IEnumerable<LightningAutomatedPayoutSettings>>($"api/v1/stores/{storeId}/payout-processors/LightningAutomatedPayoutSenderFactory{(payoutMethodId is null ? string.Empty : $"/{payoutMethodId}")}", null, HttpMethod.Get, token);
}
public virtual async Task<LightningAutomatedPayoutSettings> UpdateStoreLightningAutomatedPayoutProcessors(string storeId, string payoutMethodId, LightningAutomatedPayoutSettings request, CancellationToken token = default)
{
return await SendHttpRequest<LightningAutomatedPayoutSettings>($"api/v1/stores/{storeId}/payout-processors/LightningAutomatedPayoutSenderFactory/{payoutMethodId}", request, HttpMethod.Put, token);
}
public virtual async Task<OnChainAutomatedPayoutSettings> UpdateStoreOnChainAutomatedPayoutProcessors(string storeId, string paymentMethod, OnChainAutomatedPayoutSettings request, CancellationToken token = default)
{
return await SendHttpRequest<OnChainAutomatedPayoutSettings>($"api/v1/stores/{storeId}/payout-processors/OnChainAutomatedPayoutSenderFactory/{paymentMethod}", request, HttpMethod.Put, token);
}
public virtual async Task<IEnumerable<OnChainAutomatedPayoutSettings>> GetStoreOnChainAutomatedPayoutProcessors(string storeId, string? paymentMethod = null, CancellationToken token = default)
{
return await SendHttpRequest<IEnumerable<OnChainAutomatedPayoutSettings>>($"api/v1/stores/{storeId}/payout-processors/OnChainAutomatedPayoutSenderFactory{(paymentMethod is null ? string.Empty : $"/{paymentMethod}")}", null, HttpMethod.Get, token);
}
}

@ -4,61 +4,34 @@ using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Client.Models;
namespace BTCPayServer.Client
namespace BTCPayServer.Client;
public partial class BTCPayServerClient
{
public partial class BTCPayServerClient
public virtual async Task<StoreRateConfiguration> GetStoreRateConfiguration(string storeId, CancellationToken token = default)
{
public virtual async Task<StoreRateConfiguration> GetStoreRateConfiguration(string storeId,
CancellationToken token = default)
{
using var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/rates/configuration", method: HttpMethod.Get),
token);
return await HandleResponse<StoreRateConfiguration>(response);
}
return await SendHttpRequest<StoreRateConfiguration>($"api/v1/stores/{storeId}/rates/configuration", null, HttpMethod.Get, token);
}
public virtual async Task<List<RateSource>> GetRateSources(
CancellationToken token = default)
{
using var response = await _httpClient.SendAsync(
CreateHttpRequest($"misc/rate-sources", method: HttpMethod.Get),
token);
return await HandleResponse<List<RateSource>>(response);
}
public virtual async Task<List<RateSource>> GetRateSources(CancellationToken token = default)
{
return await SendHttpRequest<List<RateSource>>("misc/rate-sources", null, HttpMethod.Get, token);
}
public virtual async Task<StoreRateConfiguration> UpdateStoreRateConfiguration(string storeId,
StoreRateConfiguration request,
CancellationToken token = default)
{
using var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/rates/configuration", bodyPayload: request,
method: HttpMethod.Put),
token);
return await HandleResponse<StoreRateConfiguration>(response);
}
public virtual async Task<StoreRateConfiguration> UpdateStoreRateConfiguration(string storeId, StoreRateConfiguration request, CancellationToken token = default)
{
return await SendHttpRequest<StoreRateConfiguration>($"api/v1/stores/{storeId}/rates/configuration", request, HttpMethod.Put, token);
}
public virtual async Task<List<StoreRateResult>> PreviewUpdateStoreRateConfiguration(string storeId,
StoreRateConfiguration request,
string[] currencyPair,
CancellationToken token = default)
{
using var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/rates/configuration/preview", bodyPayload: request,
queryPayload: new Dictionary<string, object>() { { "currencyPair", currencyPair } },
method: HttpMethod.Post),
token);
return await HandleResponse<List<StoreRateResult>>(response);
}
public virtual async Task<List<StoreRateResult>> PreviewUpdateStoreRateConfiguration(string storeId, StoreRateConfiguration request, string[] currencyPair = null, CancellationToken token = default)
{
var queryPayload = currencyPair == null ? null : new Dictionary<string, object> { { "currencyPair", currencyPair } };
return await SendHttpRequest<StoreRateConfiguration, List<StoreRateResult>>($"api/v1/stores/{storeId}/rates/configuration/preview", queryPayload, request, HttpMethod.Post, token);
}
public virtual async Task<List<StoreRateResult>> GetStoreRates(string storeId, string[] currencyPair,
CancellationToken token = default)
{
using var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/rates",
queryPayload: new Dictionary<string, object>() { { "currencyPair", currencyPair } },
method: HttpMethod.Get),
token);
return await HandleResponse<List<StoreRateResult>>(response);
}
public virtual async Task<List<StoreRateResult>> GetStoreRates(string storeId, string[] currencyPair = null, CancellationToken token = default)
{
var queryPayload = currencyPair == null ? null : new Dictionary<string, object> { { "currencyPair", currencyPair } };
return await SendHttpRequest<List<StoreRateResult>>($"api/v1/stores/{storeId}/rates", queryPayload, HttpMethod.Get, token);
}
}

@ -5,40 +5,28 @@ using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Client.Models;
namespace BTCPayServer.Client
namespace BTCPayServer.Client;
public partial class BTCPayServerClient
{
public partial class BTCPayServerClient
public virtual async Task<List<RoleData>> GetStoreRoles(string storeId, CancellationToken token = default)
{
public virtual async Task<List<RoleData>> GetStoreRoles(string storeId,
CancellationToken token = default)
{
using var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/roles"), token);
return await HandleResponse<List<RoleData>>(response);
}
return await SendHttpRequest<List<RoleData>>($"api/v1/stores/{storeId}/roles", null, HttpMethod.Get,token);
}
public virtual async Task<IEnumerable<StoreUserData>> GetStoreUsers(string storeId,
CancellationToken token = default)
{
using var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/users"), token);
return await HandleResponse<IEnumerable<StoreUserData>>(response);
}
public virtual async Task<IEnumerable<StoreUserData>> GetStoreUsers(string storeId, CancellationToken token = default)
{
return await SendHttpRequest<IEnumerable<StoreUserData>>($"api/v1/stores/{storeId}/users", null, HttpMethod.Get, token);
}
public virtual async Task RemoveStoreUser(string storeId, string userId, CancellationToken token = default)
{
using var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/users/{userId}", method: HttpMethod.Delete), token);
await HandleResponse(response);
}
public virtual async Task RemoveStoreUser(string storeId, string userId, CancellationToken token = default)
{
await SendHttpRequest($"api/v1/stores/{storeId}/users/{userId}", null, HttpMethod.Delete, token);
}
public virtual async Task AddStoreUser(string storeId, StoreUserData request,
CancellationToken token = default)
{
if (request == null)
throw new ArgumentNullException(nameof(request));
using var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/users", bodyPayload: request, method: HttpMethod.Post),
token);
await HandleResponse(response);
}
public virtual async Task AddStoreUser(string storeId, StoreUserData request, CancellationToken token = default)
{
if (request == null) throw new ArgumentNullException(nameof(request));
await SendHttpRequest<StoreUserData>($"api/v1/stores/{storeId}/users", request, HttpMethod.Post, token);
}
}

@ -5,47 +5,45 @@ using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Client.Models;
namespace BTCPayServer.Client
namespace BTCPayServer.Client;
public partial class BTCPayServerClient
{
public partial class BTCPayServerClient
public virtual async Task<IEnumerable<StoreData>> GetStores(CancellationToken token = default)
{
public virtual async Task<IEnumerable<StoreData>> GetStores(CancellationToken token = default)
{
var response = await _httpClient.SendAsync(CreateHttpRequest("api/v1/stores"), token);
return await HandleResponse<IEnumerable<StoreData>>(response);
}
return await SendHttpRequest<IEnumerable<StoreData>>("api/v1/stores", null, HttpMethod.Get, token);
}
public virtual async Task<StoreData> GetStore(string storeId, CancellationToken token = default)
{
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}"), token);
return await HandleResponse<StoreData>(response);
}
public virtual async Task<StoreData> GetStore(string storeId, CancellationToken token = default)
{
return await SendHttpRequest<StoreData>($"api/v1/stores/{storeId}", null, HttpMethod.Get, token);
}
public virtual async Task RemoveStore(string storeId, CancellationToken token = default)
{
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}", method: HttpMethod.Delete), token);
await HandleResponse(response);
}
public virtual async Task RemoveStore(string storeId, CancellationToken token = default)
{
await SendHttpRequest($"api/v1/stores/{storeId}", null, HttpMethod.Delete, token);
}
public virtual async Task<StoreData> CreateStore(CreateStoreRequest request, CancellationToken token = default)
{
if (request == null)
throw new ArgumentNullException(nameof(request));
var response = await _httpClient.SendAsync(CreateHttpRequest("api/v1/stores", bodyPayload: request, method: HttpMethod.Post), token);
return await HandleResponse<StoreData>(response);
}
public virtual async Task<StoreData> CreateStore(CreateStoreRequest request, CancellationToken token = default)
{
if (request == null) throw new ArgumentNullException(nameof(request));
return await SendHttpRequest<StoreData>("api/v1/stores", request, HttpMethod.Post, token);
}
public virtual async Task<StoreData> UpdateStore(string storeId, UpdateStoreRequest request, CancellationToken token = default)
{
if (request == null)
throw new ArgumentNullException(nameof(request));
if (storeId == null)
throw new ArgumentNullException(nameof(storeId));
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}", bodyPayload: request, method: HttpMethod.Put), token);
return await HandleResponse<StoreData>(response);
}
public virtual async Task<StoreData> UpdateStore(string storeId, UpdateStoreRequest request, CancellationToken token = default)
{
if (request == null) throw new ArgumentNullException(nameof(request));
if (storeId == null) throw new ArgumentNullException(nameof(storeId));
return await SendHttpRequest<StoreData>($"api/v1/stores/{storeId}", request, HttpMethod.Put, token);
}
public virtual async Task<StoreData> UploadStoreLogo(string storeId, string filePath, string mimeType, CancellationToken token = default)
{
return await UploadFileRequest<StoreData>($"api/v1/stores/{storeId}/logo", filePath, mimeType, "file", HttpMethod.Post, token);
}
public virtual async Task DeleteStoreLogo(string storeId, CancellationToken token = default)
{
await SendHttpRequest($"api/v1/stores/{storeId}/logo", null, HttpMethod.Delete, token);
}
}

@ -4,52 +4,68 @@ using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Client.Models;
namespace BTCPayServer.Client
namespace BTCPayServer.Client;
public partial class BTCPayServerClient
{
public partial class BTCPayServerClient
public virtual async Task<ApplicationUserData> GetCurrentUser(CancellationToken token = default)
{
public virtual async Task<ApplicationUserData> GetCurrentUser(CancellationToken token = default)
{
var response = await _httpClient.SendAsync(CreateHttpRequest("api/v1/users/me"), token);
return await HandleResponse<ApplicationUserData>(response);
}
return await SendHttpRequest<ApplicationUserData>("api/v1/users/me", null, HttpMethod.Get, token);
}
public virtual async Task<ApplicationUserData> CreateUser(CreateApplicationUserRequest request,
CancellationToken token = default)
{
var response = await _httpClient.SendAsync(CreateHttpRequest("api/v1/users", null, request, HttpMethod.Post), token);
return await HandleResponse<ApplicationUserData>(response);
}
public virtual async Task<ApplicationUserData> UpdateCurrentUser(UpdateApplicationUserRequest request, CancellationToken token = default)
{
return await SendHttpRequest<ApplicationUserData>("api/v1/users/me", request, HttpMethod.Put, token);
}
public virtual async Task DeleteUser(string userId, CancellationToken token = default)
{
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/users/{userId}", null, HttpMethod.Delete), token);
await HandleResponse(response);
}
public virtual async Task<ApplicationUserData> UploadCurrentUserProfilePicture(string filePath, string mimeType, CancellationToken token = default)
{
return await UploadFileRequest<ApplicationUserData>("api/v1/users/me/picture", filePath, mimeType, "file", HttpMethod.Post, token);
}
public virtual async Task<ApplicationUserData> GetUserByIdOrEmail(string idOrEmail, CancellationToken token = default)
{
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/users/{idOrEmail}", null, HttpMethod.Get), token);
return await HandleResponse<ApplicationUserData>(response);
}
public virtual async Task DeleteCurrentUserProfilePicture(CancellationToken token = default)
{
await SendHttpRequest("api/v1/users/me/picture", null, HttpMethod.Delete, token);
}
public virtual async Task<ApplicationUserData> CreateUser(CreateApplicationUserRequest request, CancellationToken token = default)
{
return await SendHttpRequest<ApplicationUserData>("api/v1/users", request, HttpMethod.Post, token);
}
public virtual async Task<bool> LockUser(string idOrEmail, bool locked, CancellationToken token = default)
{
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/users/{idOrEmail}/lock", null,
new LockUserRequest { Locked = locked }, HttpMethod.Post), token);
await HandleResponse(response);
return response.IsSuccessStatusCode;
}
public virtual async Task DeleteUser(string userId, CancellationToken token = default)
{
await SendHttpRequest($"api/v1/users/{userId}", null, HttpMethod.Delete, token);
}
public virtual async Task<ApplicationUserData[]> GetUsers(CancellationToken token = default)
{
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/users/", null, HttpMethod.Get), token);
return await HandleResponse<ApplicationUserData[]>(response);
}
public virtual async Task<ApplicationUserData> GetUserByIdOrEmail(string idOrEmail, CancellationToken token = default)
{
return await SendHttpRequest<ApplicationUserData>($"api/v1/users/{idOrEmail}", null, HttpMethod.Get, token);
}
public virtual async Task DeleteCurrentUser(CancellationToken token = default)
{
await DeleteUser("me", token);
}
public virtual async Task<bool> LockUser(string idOrEmail, bool locked, CancellationToken token = default)
{
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/users/{idOrEmail}/lock", null,
new LockUserRequest { Locked = locked }, HttpMethod.Post), token);
await HandleResponse(response);
return response.IsSuccessStatusCode;
}
public virtual async Task<bool> ApproveUser(string idOrEmail, bool approved, CancellationToken token = default)
{
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/users/{idOrEmail}/approve", null,
new ApproveUserRequest { Approved = approved }, HttpMethod.Post), token);
await HandleResponse(response);
return response.IsSuccessStatusCode;
}
public virtual async Task<ApplicationUserData[]> GetUsers(CancellationToken token = default)
{
return await SendHttpRequest<ApplicationUserData[]>("api/v1/users/", null, HttpMethod.Get, token);
}
public virtual async Task DeleteCurrentUser(CancellationToken token = default)
{
await DeleteUser("me", token);
}
}

@ -3,61 +3,56 @@ using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Client.Models;
namespace BTCPayServer.Client
{
public partial class BTCPayServerClient
{
public virtual async Task<StoreWebhookData> CreateWebhook(string storeId, Client.Models.CreateStoreWebhookRequest create, CancellationToken token = default)
{
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/webhooks", bodyPayload: create, method: HttpMethod.Post), token);
return await HandleResponse<StoreWebhookData>(response);
}
public virtual async Task<StoreWebhookData> GetWebhook(string storeId, string webhookId, CancellationToken token = default)
{
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/webhooks/{webhookId}"), token);
if (response.StatusCode == System.Net.HttpStatusCode.NotFound)
return null;
return await HandleResponse<StoreWebhookData>(response);
}
public virtual async Task<StoreWebhookData> UpdateWebhook(string storeId, string webhookId, Models.UpdateStoreWebhookRequest update, CancellationToken token = default)
{
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/webhooks/{webhookId}", bodyPayload: update, method: HttpMethod.Put), token);
return await HandleResponse<StoreWebhookData>(response);
}
public virtual async Task<bool> DeleteWebhook(string storeId, string webhookId, CancellationToken token = default)
{
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/webhooks/{webhookId}", method: HttpMethod.Delete), token);
return response.IsSuccessStatusCode;
}
public virtual async Task<StoreWebhookData[]> GetWebhooks(string storeId, CancellationToken token = default)
{
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/webhooks"), token);
return await HandleResponse<StoreWebhookData[]>(response);
}
public virtual async Task<WebhookDeliveryData[]> GetWebhookDeliveries(string storeId, string webhookId, CancellationToken token = default)
{
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/webhooks/{webhookId}/deliveries"), token);
return await HandleResponse<WebhookDeliveryData[]>(response);
}
public virtual async Task<WebhookDeliveryData> GetWebhookDelivery(string storeId, string webhookId, string deliveryId, CancellationToken token = default)
{
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/webhooks/{webhookId}/deliveries/{deliveryId}"), token);
if (response.StatusCode == System.Net.HttpStatusCode.NotFound)
return null;
return await HandleResponse<WebhookDeliveryData>(response);
}
public virtual async Task<string> RedeliverWebhook(string storeId, string webhookId, string deliveryId, CancellationToken token = default)
{
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/webhooks/{webhookId}/deliveries/{deliveryId}/redeliver", null, HttpMethod.Post), token);
return await HandleResponse<string>(response);
}
namespace BTCPayServer.Client;
public virtual async Task<WebhookEvent> GetWebhookDeliveryRequest(string storeId, string webhookId, string deliveryId, CancellationToken token = default)
{
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/webhooks/{webhookId}/deliveries/{deliveryId}/request"), token);
if (response.StatusCode == System.Net.HttpStatusCode.NotFound)
return null;
return await HandleResponse<WebhookEvent>(response);
}
public partial class BTCPayServerClient
{
public virtual async Task<StoreWebhookData> CreateWebhook(string storeId, CreateStoreWebhookRequest create, CancellationToken token = default)
{
return await SendHttpRequest<StoreWebhookData>($"api/v1/stores/{storeId}/webhooks", create, HttpMethod.Post, token);
}
public virtual async Task<StoreWebhookData> GetWebhook(string storeId, string webhookId, CancellationToken token = default)
{
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/webhooks/{webhookId}"), token);
return response.StatusCode == System.Net.HttpStatusCode.NotFound ? null : await HandleResponse<StoreWebhookData>(response);
}
public virtual async Task<StoreWebhookData> UpdateWebhook(string storeId, string webhookId, UpdateStoreWebhookRequest update, CancellationToken token = default)
{
return await SendHttpRequest<StoreWebhookData>($"api/v1/stores/{storeId}/webhooks/{webhookId}", update, HttpMethod.Put, token);
}
public virtual async Task<bool> DeleteWebhook(string storeId, string webhookId, CancellationToken token = default)
{
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/webhooks/{webhookId}", method: HttpMethod.Delete), token);
return response.IsSuccessStatusCode;
}
public virtual async Task<StoreWebhookData[]> GetWebhooks(string storeId, CancellationToken token = default)
{
return await SendHttpRequest<StoreWebhookData[]>($"api/v1/stores/{storeId}/webhooks", null, HttpMethod.Get, token);
}
public virtual async Task<WebhookDeliveryData[]> GetWebhookDeliveries(string storeId, string webhookId, CancellationToken token = default)
{
return await SendHttpRequest<WebhookDeliveryData[]>($"api/v1/stores/{storeId}/webhooks/{webhookId}/deliveries", null, HttpMethod.Get, token);
}
public virtual async Task<WebhookDeliveryData> GetWebhookDelivery(string storeId, string webhookId, string deliveryId, CancellationToken token = default)
{
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/webhooks/{webhookId}/deliveries/{deliveryId}"), token);
return response.StatusCode == System.Net.HttpStatusCode.NotFound ? null : await HandleResponse<WebhookDeliveryData>(response);
}
public virtual async Task<string> RedeliverWebhook(string storeId, string webhookId, string deliveryId, CancellationToken token = default)
{
return await SendHttpRequest<string>($"api/v1/stores/{storeId}/webhooks/{webhookId}/deliveries/{deliveryId}/redeliver", null, HttpMethod.Post, token);
}
public virtual async Task<WebhookEvent> GetWebhookDeliveryRequest(string storeId, string webhookId, string deliveryId, CancellationToken token = default)
{
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/webhooks/{webhookId}/deliveries/{deliveryId}/request"), token);
return response.StatusCode == System.Net.HttpStatusCode.NotFound ? null : await HandleResponse<WebhookEvent>(response);
}
}

@ -1,6 +1,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
@ -9,154 +10,192 @@ using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace BTCPayServer.Client
namespace BTCPayServer.Client;
public partial class BTCPayServerClient
{
public partial class BTCPayServerClient
private readonly string _apiKey;
private readonly Uri _btcpayHost;
private readonly string _username;
private readonly string _password;
protected readonly HttpClient _httpClient;
public Uri Host => _btcpayHost;
public string APIKey => _apiKey;
public BTCPayServerClient(Uri btcpayHost, HttpClient httpClient = null)
{
private readonly string _apiKey;
private readonly Uri _btcpayHost;
private readonly string _username;
private readonly string _password;
private readonly HttpClient _httpClient;
public Uri Host => _btcpayHost;
if (btcpayHost == null) throw new ArgumentNullException(nameof(btcpayHost));
_btcpayHost = btcpayHost;
_httpClient = httpClient ?? new HttpClient();
}
public string APIKey => _apiKey;
public BTCPayServerClient(Uri btcpayHost, string APIKey, HttpClient httpClient = null)
{
_apiKey = APIKey;
_btcpayHost = btcpayHost;
_httpClient = httpClient ?? new HttpClient();
}
public BTCPayServerClient(Uri btcpayHost, HttpClient httpClient = null)
{
if (btcpayHost == null)
throw new ArgumentNullException(nameof(btcpayHost));
_btcpayHost = btcpayHost;
_httpClient = httpClient ?? new HttpClient();
}
public BTCPayServerClient(Uri btcpayHost, string APIKey, HttpClient httpClient = null)
{
_apiKey = APIKey;
_btcpayHost = btcpayHost;
_httpClient = httpClient ?? new HttpClient();
}
public BTCPayServerClient(Uri btcpayHost, string username, string password, HttpClient httpClient = null)
{
_apiKey = APIKey;
_btcpayHost = btcpayHost;
_username = username;
_password = password;
_httpClient = httpClient ?? new HttpClient();
}
public BTCPayServerClient(Uri btcpayHost, string username, string password, HttpClient httpClient = null)
protected async Task HandleResponse(HttpResponseMessage message)
{
if (!message.IsSuccessStatusCode && message.Content?.Headers?.ContentType?.MediaType?.StartsWith("application/json", StringComparison.OrdinalIgnoreCase) is true)
{
_apiKey = APIKey;
_btcpayHost = btcpayHost;
_username = username;
_password = password;
_httpClient = httpClient ?? new HttpClient();
}
protected async Task HandleResponse(HttpResponseMessage message)
{
if (!message.IsSuccessStatusCode && message.Content?.Headers?.ContentType?.MediaType?.StartsWith("application/json", StringComparison.OrdinalIgnoreCase) is true)
if (message.StatusCode == System.Net.HttpStatusCode.UnprocessableEntity)
{
if (message.StatusCode == System.Net.HttpStatusCode.UnprocessableEntity)
{
var aa = await message.Content.ReadAsStringAsync();
var err = JsonConvert.DeserializeObject<Models.GreenfieldValidationError[]>(aa);
throw new GreenfieldValidationException(err);
}
if (message.StatusCode == System.Net.HttpStatusCode.Forbidden)
{
var err = JsonConvert.DeserializeObject<Models.GreenfieldPermissionAPIError>(await message.Content.ReadAsStringAsync());
throw new GreenfieldAPIException((int)message.StatusCode, err);
}
else
{
var err = JsonConvert.DeserializeObject<Models.GreenfieldAPIError>(await message.Content.ReadAsStringAsync());
if (err.Code != null)
throw new GreenfieldAPIException((int)message.StatusCode, err);
}
var aa = await message.Content.ReadAsStringAsync();
var err = JsonConvert.DeserializeObject<Models.GreenfieldValidationError[]>(aa);
throw new GreenfieldValidationException(err);
}
message.EnsureSuccessStatusCode();
}
protected async Task<T> HandleResponse<T>(HttpResponseMessage message)
{
await HandleResponse(message);
var str = await message.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<T>(str);
}
public async Task<T> SendHttpRequest<T>(string path,
Dictionary<string, object> queryPayload = null,
HttpMethod method = null, CancellationToken cancellationToken = default)
{
using var resp = await _httpClient.SendAsync(CreateHttpRequest(path, queryPayload, method), cancellationToken);
return await HandleResponse<T>(resp);
}
public async Task<T> SendHttpRequest<T>(string path,
object bodyPayload = null,
HttpMethod method = null, CancellationToken cancellationToken = default)
{
using var resp = await _httpClient.SendAsync(CreateHttpRequest(path: path, bodyPayload: bodyPayload, method: method), cancellationToken);
return await HandleResponse<T>(resp);
}
protected virtual HttpRequestMessage CreateHttpRequest(string path,
Dictionary<string, object> queryPayload = null,
HttpMethod method = null)
{
UriBuilder uriBuilder = new UriBuilder(_btcpayHost) { Path = path };
if (queryPayload != null && queryPayload.Any())
if (message.StatusCode == System.Net.HttpStatusCode.Forbidden)
{
AppendPayloadToQuery(uriBuilder, queryPayload);
}
var httpRequest = new HttpRequestMessage(method ?? HttpMethod.Get, uriBuilder.Uri);
if (_apiKey != null)
httpRequest.Headers.Authorization = new AuthenticationHeaderValue("token", _apiKey);
else if (!string.IsNullOrEmpty(_username))
{
httpRequest.Headers.Authorization = new AuthenticationHeaderValue("Basic", System.Convert.ToBase64String(Encoding.ASCII.GetBytes(_username + ":" + _password)));
}
return httpRequest;
}
protected virtual HttpRequestMessage CreateHttpRequest<T>(string path,
Dictionary<string, object> queryPayload = null,
T bodyPayload = default, HttpMethod method = null)
{
var request = CreateHttpRequest(path, queryPayload, method);
if (typeof(T).IsPrimitive || !EqualityComparer<T>.Default.Equals(bodyPayload, default(T)))
{
request.Content = new StringContent(JsonConvert.SerializeObject(bodyPayload), Encoding.UTF8, "application/json");
}
return request;
}
public static void AppendPayloadToQuery(UriBuilder uri, KeyValuePair<string, object> keyValuePair)
{
if (uri.Query.Length > 1)
uri.Query += "&";
UriBuilder uriBuilder = uri;
if (!(keyValuePair.Value is string) &&
keyValuePair.Value.GetType().GetInterfaces().Contains((typeof(IEnumerable))))
{
foreach (var item in (IEnumerable)keyValuePair.Value)
{
uriBuilder.Query = uriBuilder.Query + Uri.EscapeDataString(keyValuePair.Key) + "=" +
Uri.EscapeDataString(item.ToString()) + "&";
}
var err = JsonConvert.DeserializeObject<Models.GreenfieldPermissionAPIError>(await message.Content.ReadAsStringAsync());
throw new GreenfieldAPIException((int)message.StatusCode, err);
}
else
{
uriBuilder.Query = uriBuilder.Query + Uri.EscapeDataString(keyValuePair.Key) + "=" +
Uri.EscapeDataString(keyValuePair.Value.ToString()) + "&";
var err = JsonConvert.DeserializeObject<Models.GreenfieldAPIError>(await message.Content.ReadAsStringAsync());
if (err.Code != null)
throw new GreenfieldAPIException((int)message.StatusCode, err);
}
uri.Query = uri.Query.Trim('&');
}
message.EnsureSuccessStatusCode();
}
protected virtual async Task<T> HandleResponse<T>(HttpResponseMessage message)
{
await HandleResponse(message);
var str = await message.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<T>(str);
}
public virtual async Task SendHttpRequest(string path,
Dictionary<string, object> queryPayload = null,
HttpMethod method = null, CancellationToken cancellationToken = default)
{
using var resp = await _httpClient.SendAsync(CreateHttpRequest(path, queryPayload, method), cancellationToken);
await HandleResponse(resp);
}
public virtual async Task<T> SendHttpRequest<T>(string path,
Dictionary<string, object> queryPayload = null,
HttpMethod method = null, CancellationToken cancellationToken = default)
{
using var resp = await _httpClient.SendAsync(CreateHttpRequest(path, queryPayload, method), cancellationToken);
return await HandleResponse<T>(resp);
}
public virtual async Task SendHttpRequest(string path,
object bodyPayload = null,
HttpMethod method = null, CancellationToken cancellationToken = default)
{
using var resp = await _httpClient.SendAsync(CreateHttpRequest(path: path, bodyPayload: bodyPayload, method: method), cancellationToken);
await HandleResponse(resp);
}
protected virtual async Task<T> SendHttpRequest<T>(string path,
object bodyPayload = null,
HttpMethod method = null, CancellationToken cancellationToken = default)
{
using var resp = await _httpClient.SendAsync(CreateHttpRequest(path: path, bodyPayload: bodyPayload, method: method), cancellationToken);
return await HandleResponse<T>(resp);
}
protected virtual async Task<TRes> SendHttpRequest<TReq, TRes>(string path,
Dictionary<string, object> queryPayload = null,
TReq bodyPayload = default, HttpMethod method = null, CancellationToken cancellationToken = default)
{
using var resp = await _httpClient.SendAsync(CreateHttpRequest(path: path, bodyPayload: bodyPayload, queryPayload: queryPayload, method: method), cancellationToken);
return await HandleResponse<TRes>(resp);
}
protected virtual HttpRequestMessage CreateHttpRequest(string path,
Dictionary<string, object> queryPayload = null,
HttpMethod method = null)
{
var uriBuilder = new UriBuilder(_btcpayHost);
uriBuilder.Path += (uriBuilder.Path.EndsWith("/") || path.StartsWith("/") ? "" : "/") + path;
if (queryPayload != null && queryPayload.Any())
{
AppendPayloadToQuery(uriBuilder, queryPayload);
}
public static void AppendPayloadToQuery(UriBuilder uri, Dictionary<string, object> payload)
var httpRequest = new HttpRequestMessage(method ?? HttpMethod.Get, uriBuilder.Uri);
if (_apiKey != null)
httpRequest.Headers.Authorization = new AuthenticationHeaderValue("token", _apiKey);
else if (!string.IsNullOrEmpty(_username))
{
if (uri.Query.Length > 1)
uri.Query += "&";
foreach (KeyValuePair<string, object> keyValuePair in payload)
httpRequest.Headers.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.ASCII.GetBytes(_username + ":" + _password)));
}
return httpRequest;
}
protected virtual HttpRequestMessage CreateHttpRequest<T>(string path,
Dictionary<string, object> queryPayload = null,
T bodyPayload = default, HttpMethod method = null)
{
var request = CreateHttpRequest(path, queryPayload, method);
if (typeof(T).IsPrimitive || !EqualityComparer<T>.Default.Equals(bodyPayload, default(T)))
{
request.Content = new StringContent(JsonConvert.SerializeObject(bodyPayload), Encoding.UTF8, "application/json");
}
return request;
}
protected virtual async Task<T> UploadFileRequest<T>(string apiPath, string filePath, string mimeType, string formFieldName, HttpMethod method = null, CancellationToken token = default)
{
using MultipartFormDataContent multipartContent = new();
var fileContent = new StreamContent(File.OpenRead(filePath));
var fileName = Path.GetFileName(filePath);
fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse(mimeType);
multipartContent.Add(fileContent, formFieldName, fileName);
var req = CreateHttpRequest(apiPath, null, method ?? HttpMethod.Post);
req.Content = multipartContent;
using var resp = await _httpClient.SendAsync(req, token);
return await HandleResponse<T>(resp);
}
public static void AppendPayloadToQuery(UriBuilder uri, KeyValuePair<string, object> keyValuePair)
{
if (uri.Query.Length > 1)
uri.Query += "&";
UriBuilder uriBuilder = uri;
if (!(keyValuePair.Value is string) &&
keyValuePair.Value.GetType().GetInterfaces().Contains((typeof(IEnumerable))))
{
foreach (var item in (IEnumerable)keyValuePair.Value)
{
AppendPayloadToQuery(uri, keyValuePair);
uriBuilder.Query = uriBuilder.Query + Uri.EscapeDataString(keyValuePair.Key) + "=" +
Uri.EscapeDataString(item.ToString()) + "&";
}
}
else
{
uriBuilder.Query = uriBuilder.Query + Uri.EscapeDataString(keyValuePair.Key) + "=" +
Uri.EscapeDataString(keyValuePair.Value.ToString()) + "&";
}
uri.Query = uri.Query.Trim('&');
}
public static void AppendPayloadToQuery(UriBuilder uri, Dictionary<string, object> payload)
{
if (uri.Query.Length > 1)
uri.Query += "&";
foreach (KeyValuePair<string, object> keyValuePair in payload)
{
AppendPayloadToQuery(uri, keyValuePair);
}
}
}

@ -1,17 +1,15 @@
using System;
namespace BTCPayServer.Client
namespace BTCPayServer.Client;
public class GreenfieldAPIException : Exception
{
public class GreenfieldAPIException : Exception
public GreenfieldAPIException(int httpCode, Models.GreenfieldAPIError error) : base(error.Message)
{
public GreenfieldAPIException(int httpCode, Models.GreenfieldAPIError error) : base(error.Message)
{
if (error == null)
throw new ArgumentNullException(nameof(error));
HttpCode = httpCode;
APIError = error;
}
public Models.GreenfieldAPIError APIError { get; }
public int HttpCode { get; set; }
if (error == null) throw new ArgumentNullException(nameof(error));
HttpCode = httpCode;
APIError = error;
}
public Models.GreenfieldAPIError APIError { get; }
public int HttpCode { get; set; }
}

@ -2,27 +2,25 @@ using System;
using System.Text;
using BTCPayServer.Client.Models;
namespace BTCPayServer.Client
namespace BTCPayServer.Client;
public class GreenfieldValidationException : Exception
{
public class GreenfieldValidationException : Exception
public GreenfieldValidationException(GreenfieldValidationError[] errors) : base(BuildMessage(errors))
{
public GreenfieldValidationException(Models.GreenfieldValidationError[] errors) : base(BuildMessage(errors))
{
ValidationErrors = errors;
}
private static string BuildMessage(GreenfieldValidationError[] errors)
{
if (errors == null)
throw new ArgumentNullException(nameof(errors));
StringBuilder builder = new StringBuilder();
foreach (var error in errors)
{
builder.AppendLine($"{error.Path}: {error.Message}");
}
return builder.ToString();
}
public Models.GreenfieldValidationError[] ValidationErrors { get; }
ValidationErrors = errors;
}
private static string BuildMessage(GreenfieldValidationError[] errors)
{
if (errors == null) throw new ArgumentNullException(nameof(errors));
var builder = new StringBuilder();
foreach (var error in errors)
{
builder.AppendLine($"{error.Path}: {error.Message}");
}
return builder.ToString();
}
public GreenfieldValidationError[] ValidationErrors { get; }
}

@ -0,0 +1,43 @@
using NBitcoin;
using NBitcoin.DataEncoders;
using NBitcoin.JsonConverters;
using Newtonsoft.Json;
using System;
using System.IO;
using System.Reflection;
namespace BTCPayServer.Client.JsonConverters
{
public class SaneOutpointJsonConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(OutPoint).GetTypeInfo().IsAssignableFrom(objectType.GetTypeInfo());
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
if (reader.TokenType != JsonToken.String)
throw new JsonObjectException($"Unexpected json token type, expected is {JsonToken.String} and actual is {reader.TokenType}", reader);
try
{
if (!OutPoint.TryParse((string)reader.Value, out var outpoint))
throw new JsonObjectException("Invalid bitcoin object of type OutPoint", reader);
return outpoint;
}
catch (EndOfStreamException)
{
}
throw new JsonObjectException("Invalid bitcoin object of type OutPoint", reader);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
if (value is { })
writer.WriteValue(value.ToString());
}
}
}

@ -1,4 +1,5 @@
using System;
using System.Globalization;
using NBitcoin.JsonConverters;
using Newtonsoft.Json;
@ -58,6 +59,8 @@ namespace BTCPayServer.Client.JsonConverters
return null;
return TimeSpan.Zero;
}
if (reader.TokenType == JsonToken.String && TimeSpan.TryParse(reader.Value?.ToString(), CultureInfo.InvariantCulture, out var res))
return res;
if (reader.TokenType != JsonToken.Integer)
throw new JsonObjectException("Invalid timespan, expected integer", reader);
return ToTimespan((long)reader.Value);

@ -1,36 +0,0 @@
using System;
using System.Globalization;
using BTCPayServer.Client.Models;
using BTCPayServer.Lightning;
using NBitcoin.JsonConverters;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace BTCPayServer.Client.JsonConverters
{
public class TradeQuantityJsonConverter : JsonConverter<TradeQuantity>
{
public override TradeQuantity ReadJson(JsonReader reader, Type objectType, TradeQuantity existingValue, bool hasExistingValue, JsonSerializer serializer)
{
JToken token = JToken.Load(reader);
switch (token.Type)
{
case JTokenType.Float:
case JTokenType.Integer:
case JTokenType.String:
if (TradeQuantity.TryParse(token.ToString(), out var q))
return q;
break;
case JTokenType.Null:
return null;
}
throw new JsonObjectException("Invalid TradeQuantity, expected string. Expected: \"1.50\" or \"50%\"", reader);
}
public override void WriteJson(JsonWriter writer, TradeQuantity value, JsonSerializer serializer)
{
if (value is not null)
writer.WriteValue(value.ToString());
}
}
}

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace BTCPayServer.Client.Models;
public class AppBaseData
{
public string Id { get; set; }
public string AppType { get; set; }
public string AppName { get; set; }
public string StoreId { get; set; }
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public bool? Archived { get; set; }
[JsonConverter(typeof(NBitcoin.JsonConverters.DateTimeToUnixTimeConverter))]
public DateTimeOffset Created { get; set; }
[JsonExtensionData]
public IDictionary<string, JToken> AdditionalData { get; set; } = new Dictionary<string, JToken>();
}
public interface IAppRequest
{
public string AppName { get; set; }
}

@ -0,0 +1,15 @@
using BTCPayServer.JsonConverters;
using Newtonsoft.Json;
namespace BTCPayServer.Client.Models;
public class AppItemStats
{
public string ItemCode { get; set; }
public string Title { get; set; }
public int SalesCount { get; set; }
[JsonConverter(typeof(NumericStringJsonConverter))]
public decimal Total { get; set; }
public string TotalFormatted { get; set; }
}

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
namespace BTCPayServer.Client.Models;
public class AppSalesStats
{
public int SalesCount { get; set; }
public IEnumerable<AppSalesStatsItem> Series { get; set; }
}
public class AppSalesStatsItem
{
[JsonConverter(typeof(NBitcoin.JsonConverters.DateTimeToUnixTimeConverter))]
public DateTime Date { get; set; }
public string Label { get; set; }
public int SalesCount { get; set; }
}

@ -14,6 +14,16 @@ namespace BTCPayServer.Client.Models
/// the email AND username of the user
/// </summary>
public string Email { get; set; }
/// <summary>
/// the name of the user
/// </summary>
public string Name { get; set; }
/// <summary>
/// the image url of the user
/// </summary>
public string ImageUrl { get; set; }
/// <summary>
/// Whether the user has verified their email
@ -25,6 +35,16 @@ namespace BTCPayServer.Client.Models
/// </summary>
public bool RequiresEmailConfirmation { get; set; }
/// <summary>
/// Whether the user was approved by an admin
/// </summary>
public bool Approved { get; set; }
/// <summary>
/// whether the user needed approval on account creation
/// </summary>
public bool RequiresApproval { get; set; }
/// <summary>
/// the roles of the user
/// </summary>

@ -0,0 +1,6 @@
namespace BTCPayServer.Client;
public class ApproveUserRequest
{
public bool Approved { get; set; }
}

@ -1,87 +0,0 @@
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
namespace BTCPayServer.Client.Models
{
public enum PosViewType
{
Static,
Cart,
Light,
Print
}
public class CreateAppRequest
{
public string AppName { get; set; }
public string AppType { get; set; }
}
public class CreatePointOfSaleAppRequest : CreateAppRequest
{
public string Currency { get; set; } = null;
public string Title { get; set; } = null;
public string Description { get; set; } = null;
public string Template { get; set; } = null;
[JsonConverter(typeof(StringEnumConverter))]
public PosViewType DefaultView { get; set; }
public bool ShowCustomAmount { get; set; } = false;
public bool ShowDiscount { get; set; } = false;
public bool ShowSearch { get; set; } = true;
public bool ShowCategories { get; set; } = true;
public bool EnableTips { get; set; } = false;
public string CustomAmountPayButtonText { get; set; } = null;
public string FixedAmountPayButtonText { get; set; } = null;
public string TipText { get; set; } = null;
public string CustomCSSLink { get; set; } = null;
public string NotificationUrl { get; set; } = null;
public string RedirectUrl { get; set; } = null;
public bool? RedirectAutomatically { get; set; } = null;
public bool? RequiresRefundEmail { get; set; } = null;
public bool? Archived { get; set; } = null;
public string FormId { get; set; } = null;
public string EmbeddedCSS { get; set; } = null;
}
public enum CrowdfundResetEvery
{
Never,
Hour,
Day,
Month,
Year
}
public class CreateCrowdfundAppRequest : CreateAppRequest
{
public string Title { get; set; } = null;
public bool? Enabled { get; set; } = null;
public bool? EnforceTargetAmount { get; set; } = null;
[JsonConverter(typeof(NBitcoin.JsonConverters.DateTimeToUnixTimeConverter))]
public DateTimeOffset? StartDate { get; set; } = null;
public string TargetCurrency { get; set; } = null;
public string Description { get; set; } = null;
[JsonConverter(typeof(NBitcoin.JsonConverters.DateTimeToUnixTimeConverter))]
public DateTimeOffset? EndDate { get; set; } = null;
public decimal? TargetAmount { get; set; } = null;
public string CustomCSSLink { get; set; } = null;
public string MainImageUrl { get; set; } = null;
public string EmbeddedCSS { get; set; } = null;
public string NotificationUrl { get; set; } = null;
public string Tagline { get; set; } = null;
public string PerksTemplate { get; set; } = null;
public bool? SoundsEnabled { get; set; } = null;
public string DisqusShortname { get; set; } = null;
public bool? AnimationsEnabled { get; set; } = null;
public int? ResetEveryAmount { get; set; } = null;
[JsonConverter(typeof(StringEnumConverter))]
public CrowdfundResetEvery ResetEvery { get; set; } = CrowdfundResetEvery.Never;
public bool? DisplayPerksValue { get; set; } = null;
public bool? DisplayPerksRanking { get; set; } = null;
public bool? SortPerksByPopularity { get; set; } = null;
public bool? Archived { get; set; } = null;
public string[] Sounds { get; set; } = null;
public string[] AnimationColors { get; set; } = null;
}
}

@ -2,6 +2,16 @@ namespace BTCPayServer.Client.Models
{
public class CreateApplicationUserRequest
{
/// <summary>
/// the name of the new user
/// </summary>
public string Name { get; set; }
/// <summary>
/// the image url of the new user
/// </summary>
public string ImageUrl { get; set; }
/// <summary>
/// the email AND username of the new user
/// </summary>

@ -1,12 +0,0 @@
using Newtonsoft.Json.Linq;
namespace BTCPayServer.Client.Models
{
public class CreateCustodianAccountRequest
{
public string CustodianCode { get; set; }
public string Name { get; set; }
public JObject Config { get; set; }
}
}

@ -1,4 +1,5 @@
using System.Collections.Generic;
using BTCPayServer.Client.JsonConverters;
using BTCPayServer.JsonConverters;
using NBitcoin;
using NBitcoin.JsonConverters;
@ -21,7 +22,7 @@ namespace BTCPayServer.Client.Models
public bool ProceedWithPayjoin { get; set; } = true;
public bool ProceedWithBroadcast { get; set; } = true;
public bool NoChange { get; set; } = false;
[JsonProperty(ItemConverterType = typeof(OutpointJsonConverter))]
[JsonProperty(ItemConverterType = typeof(SaneOutpointJsonConverter))]
public List<OutPoint> SelectedInputs { get; set; } = null;
public List<CreateOnChainTransactionRequestDestination> Destinations { get; set; }
[JsonProperty("rbf")]

@ -8,6 +8,6 @@ namespace BTCPayServer.Client.Models
public string Destination { get; set; }
[JsonConverter(typeof(NumericStringJsonConverter))]
public decimal? Amount { get; set; }
public string PaymentMethod { get; set; }
public string PayoutMethodId { get; set; }
}
}

@ -12,8 +12,6 @@ namespace BTCPayServer.Client.Models
[JsonProperty(ItemConverterType = typeof(NumericStringJsonConverter))]
public decimal Amount { get; set; }
public string Currency { get; set; }
[JsonConverter(typeof(TimeSpanJsonConverter.Seconds))]
public TimeSpan? Period { get; set; }
[JsonConverter(typeof(TimeSpanJsonConverter.Days))]
[JsonProperty("BOLT11Expiration")]
public TimeSpan? BOLT11Expiration { get; set; }
@ -21,7 +19,7 @@ namespace BTCPayServer.Client.Models
public DateTimeOffset? ExpiresAt { get; set; }
[JsonConverter(typeof(NBitcoin.JsonConverters.DateTimeToUnixTimeConverter))]
public DateTimeOffset? StartsAt { get; set; }
public string[] PaymentMethods { get; set; }
public string[] PayoutMethods { get; set; }
public bool AutoApproveClaims { get; set; }
}
}

@ -0,0 +1,55 @@
#nullable enable
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
namespace BTCPayServer.Client.Models;
public abstract class CrowdfundBaseData : AppBaseData
{
public string? Title { get; set; }
public bool? Enabled { get; set; }
public bool? EnforceTargetAmount { get; set; }
[JsonConverter(typeof(NBitcoin.JsonConverters.DateTimeToUnixTimeConverter))]
public DateTimeOffset? StartDate { get; set; }
public string? TargetCurrency { get; set; }
public string? Description { get; set; }
[JsonConverter(typeof(NBitcoin.JsonConverters.DateTimeToUnixTimeConverter))]
public DateTimeOffset? EndDate { get; set; }
public decimal? TargetAmount { get; set; }
public string? MainImageUrl { get; set; }
public string? NotificationUrl { get; set; }
public string? Tagline { get; set; }
public bool? DisqusEnabled { get; set; }
public string? DisqusShortname { get; set; }
public bool? SoundsEnabled { get; set; }
public bool? AnimationsEnabled { get; set; }
public int? ResetEveryAmount { get; set; }
[JsonConverter(typeof(StringEnumConverter))]
public CrowdfundResetEvery? ResetEvery { get; set; }
public bool? DisplayPerksValue { get; set; }
public bool? DisplayPerksRanking { get; set; }
public bool? SortPerksByPopularity { get; set; }
public string[]? Sounds { get; set; }
public string[]? AnimationColors { get; set; }
public string? FormId { get; set; }
}
public class CrowdfundAppData : CrowdfundBaseData
{
public object? Perks { get; set; }
}
public class CrowdfundAppRequest : CrowdfundBaseData, IAppRequest
{
public string? PerksTemplate { get; set; }
}
public enum CrowdfundResetEvery
{
Never,
Hour,
Day,
Month,
Year
}

@ -1,16 +0,0 @@
using Newtonsoft.Json.Linq;
namespace BTCPayServer.Client.Models
{
public abstract class CustodianAccountBaseData
{
public string CustodianCode { get; set; }
public string Name { get; set; }
public string StoreId { get; set; }
public JObject Config { get; set; }
}
}

@ -1,7 +0,0 @@
namespace BTCPayServer.Client.Models
{
public class CustodianAccountData : CustodianAccountBaseData
{
public string Id { get; set; }
}
}

@ -1,14 +0,0 @@
using System.Collections.Generic;
namespace BTCPayServer.Client.Models;
public class CustodianAccountResponse : CustodianAccountData
{
public IDictionary<string, decimal> AssetBalances { get; set; }
public CustodianAccountResponse()
{
}
}

@ -1,13 +0,0 @@
using System.Collections.Generic;
namespace BTCPayServer.Client.Models;
public class CustodianData
{
public string Code { get; set; }
public string Name { get; set; }
public Dictionary<string, AssetPairData> TradableAssetPairs { get; set; }
public string[] WithdrawablePaymentMethods { get; set; }
public string[] DepositablePaymentMethods { get; set; }
}

@ -1,15 +0,0 @@
namespace BTCPayServer.Client.Models;
public class DepositAddressData
{
// /**
// * Example: P2PKH, P2SH, P2WPKH, P2TR, BOLT11, ...
// */
// public string Type { get; set; }
/**
* Format depends hugely on the type.
*/
public string Address { get; set; }
}

@ -1,3 +1,5 @@
using Newtonsoft.Json;
namespace BTCPayServer.Client.Models;
public class EmailSettingsData
@ -26,4 +28,11 @@ public class EmailSettingsData
get; set;
}
public bool DisableCertificateCheck { get; set; }
[JsonIgnore]
public bool EnabledCertificateCheck
{
get => !DisableCertificateCheck;
set { DisableCertificateCheck = !value; }
}
}

@ -0,0 +1,16 @@
using System;
using Newtonsoft.Json;
namespace BTCPayServer.Client.Models;
public class FileData
{
public string Id { get; set; }
public string UserId { get; set; }
public string Uri { get; set; }
public string Url { get; set; }
public string OriginalName { get; set; }
public string StorageName { get; set; }
[JsonConverter(typeof(NBitcoin.JsonConverters.DateTimeToUnixTimeConverter))]
public DateTimeOffset? CreatedAt { get; set; }
}

@ -1,12 +1,16 @@
using System.Collections.Generic;
using BTCPayServer.Client.JsonConverters;
using BTCPayServer.Client.Models;
using NBitcoin;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Linq;
namespace BTCPayServer.Client
{
public class GenerateOnChainWalletRequest
{
public string Label { get; set; }
public int AccountNumber { get; set; } = 0;
[JsonConverter(typeof(MnemonicJsonConverter))]
public Mnemonic ExistingMnemonic { get; set; }
@ -22,4 +26,17 @@ namespace BTCPayServer.Client
public bool ImportKeysToRPC { get; set; }
public bool SavePrivateKeys { get; set; }
}
public class GenerateOnChainWalletResponse : GenericPaymentMethodData
{
public class ConfigData
{
public string Label { get; set; }
public string AccountDerivation { get; set; }
[JsonExtensionData]
IDictionary<string, JToken> AdditionalData { get; set; }
}
[JsonConverter(typeof(MnemonicJsonConverter))]
public Mnemonic Mnemonic { get; set; }
public new ConfigData Config { get; set; }
}
}

@ -1,9 +1,22 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace BTCPayServer.Client.Models
{
public class GenericPaymentMethodData
{
public bool Enabled { get; set; }
public object Data { get; set; }
public string CryptoCode { get; set; }
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public JToken Config { get; set; }
public string PaymentMethodId { get; set; }
}
public class UpdatePaymentMethodRequest
{
public UpdatePaymentMethodRequest()
{
}
public bool? Enabled { get; set; }
public JToken Config { get; set; }
}
}

@ -11,8 +11,7 @@ namespace BTCPayServer.Client.Models
public GreenfieldAPIError(string code, string message)
{
code = code ?? "generic-error";
if (message == null)
throw new ArgumentNullException(nameof(message));
if (message == null) throw new ArgumentNullException(nameof(message));
Code = code;
Message = message;
}

@ -6,14 +6,12 @@ namespace BTCPayServer.Client.Models
{
public GreenfieldValidationError()
{
}
public GreenfieldValidationError(string path, string message)
{
if (path == null)
throw new ArgumentNullException(nameof(path));
if (message == null)
throw new ArgumentNullException(nameof(message));
if (path == null) throw new ArgumentNullException(nameof(path));
if (message == null) throw new ArgumentNullException(nameof(message));
Path = path;
Message = message;
}

@ -83,9 +83,7 @@ namespace BTCPayServer.Client.Models
public string RedirectURL { get; set; }
public bool? RedirectAutomatically { get; set; }
public bool? RequiresRefundEmail { get; set; } = null;
public string DefaultLanguage { get; set; }
public CheckoutType? CheckoutType { get; set; }
public bool? LazyPaymentMethods { get; set; }
}
}

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