Compare commits

..

904 Commits

Author SHA1 Message Date
Nicolas Dorier
4a31cf0a09 Migrate payment requests (#6260) 2024-10-01 16:07:51 +09:00
d11n
82620ee327 Move wallet payment settings back to store settings (#6251)
Intermediate solution, until we implement these settings on the payment method level. Closes #6237.
2024-09-30 19:13:51 +09:00
nicolas.dorier
6d284b4124 Give time for pollers to detect payments after server restart 2024-09-27 15:48:16 +09:00
Chukwuleta Tobechi
83fa8cbf0f prevent app creation without wallet creation (#6255)
* prevent app creation without wallet creation

* resolve test failures

* resolve selenium test
2024-09-27 15:28:55 +09:00
Nicolas Dorier
9ba4b030ed Fix: Do not expose xpub without modify store permission (#6212) 2024-09-27 15:27:04 +09:00
d11n
272cc3d3c9 POS: Option for user sign in via the QR code (#6231)
* 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
nicolas.dorier
b5590a38fe Add better error message if v1 routes are used. 2024-09-26 19:09:27 +09:00
d11n
443a350bad App Service: Validate IDs when parsing items template (#6228)
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 #6227.
2024-09-26 15:52:16 +09:00
nicolas.dorier
7013e618de Remove dead fields from swagger 2024-09-26 12:23:41 +09:00
Nicolas Dorier
363b60385b Renaming various properties in the Payouts API (#6246)
* Rename Payouts Currency/OriginalCurrency

* Rename Payout Processor PayoutMethodIds

* Rename paymentMethods to payoutMethodIds

* Rename payoutMethodIds to payoutMethods
2024-09-26 11:25:45 +09:00
nicolas.dorier
90635ffc4e Remove BTCPAY_EXPERIMENTALV2_CONFIRM 2024-09-25 23:11:53 +09:00
Nicolas Dorier
056f850268 Optimize load time of StoreRoles related pages/routes (#6245) 2024-09-25 23:10:13 +09:00
nicolas.dorier
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
nicolas.dorier
e16b4062b5 Fix payout processor migration 2024-09-25 18:50:49 +09:00
Nicolas Dorier
747dacf3b1 Consolidate migrations from alpha (#6244) 2024-09-25 18:23:10 +09:00
nicolas.dorier
f00a71922f Optimize queries from payout processor at startup 2024-09-24 23:39:05 +09:00
nicolas.dorier
c97c9d4ece Add SQL test for GetMonitoredInvoices 2024-09-24 22:07:02 +09:00
nicolas.dorier
9d3f8672d9 Fix GetMonitoredInvoices 2024-09-24 17:21:36 +09:00
Vincent Bouzon
fe48cd4236 fix InvoiceRepository.GetMonitoredInvoices (#6243) 2024-09-24 15:44:51 +09:00
nicolas.dorier
587d3aa612 Fix query 2024-09-24 09:52:28 +09:00
nicolas.dorier
8a951940fd Remove dead property 2024-09-24 09:47:46 +09:00
nicolas.dorier
b726ef8a2e Migrate PayoutProcessors's PayoutMethodId in entity migration 2024-09-24 09:43:02 +09:00
nicolas.dorier
25e360e175 Allow listeners to retrieve invoices with nonActivated prompts 2024-09-24 08:43:30 +09:00
Nicolas Dorier
1d9ec253fb Fix migration of Invoice's payment (#6241) 2024-09-23 23:59:18 +09:00
Nicolas Dorier
3cf1aa00fa Payments should use composite key (#6240)
* Payments should use composite key

* Invert PK for InvoiceAddress
2024-09-23 17:06:56 +09:00
nicolas.dorier
36a5d0ee3f Fix: Monero and ZCash not tracking addresses 2024-09-22 11:13:09 +09:00
Nicolas Dorier
f5e5174045 Refactor: Add GetMonitoredInvoices to fetch pending invoices or those with pending payments (#6235) 2024-09-20 18:54:36 +09:00
Nicolas Dorier
ba2301ebfe Refactor the InvoiceAddresses table (#6232) 2024-09-19 22:15:02 +09:00
nicolas.dorier
df651a2157 Fix GetInvoicesWithPendingPayments 2024-09-18 18:11:30 +09:00
jackstar12
2d2c1d5f2d fix: check lightning payment status (#6219) 2024-09-17 21:41:04 +09:00
Nicolas Dorier
0f93581ff5 Refactor confirmation count tracking (#6215) 2024-09-17 17:28:58 +09:00
rockstardev
397452a7fe Setting standard 150x100 size for Unbank logo 2024-09-16 16:38:04 -05:00
rockstardev
cd3157361a Adding Unbank as BTCPay Server Foundation Supporter (#6225) 2024-09-16 16:33:11 -05:00
nicolas.dorier
29a89f185a Fix: Not able to change SpeedPolicy of a store 2024-09-13 22:59:14 +09:00
d11n
2f7a5c2967 Wallet: Generate receive address automatically (#6122)
* 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
Chukwuleta Tobechi
f07ed53f7e Handle password reset when SMTP isn't configured or validated (#6150)
* 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
d11n
7348a6a62f Store Branding: Apply brand color to backend as well (#5992)
* Store Branding: Apply brand color to backend as well

Closes #5990.

* 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
d11n
b7ba53eb60 UI: Fix for truncate center component (#6213) 2024-09-13 17:55:31 +09:00
Dennis Reimann
e389d6a96b UI: Minor fix on policies page 2024-09-12 18:43:52 +02:00
d11n
0238dffc7a POS: Fix accounting for manually entered keypad amounts (#6178)
* 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 #6168.

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

* Rewrite old API path to new API

* Rename routes
2024-09-12 15:19:10 +09:00
Nicolas Dorier
b4946f4db1 Fix divisibility in invoice details of lightning amounts (#6202)
* 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
d11n
f3d485da53 Invitation process improvements (#6188)
* Server: Make sending email optional when adding user

Closes #6158.

* Generate custom invite token and store it in user blob

Closes btcpayserver/app/#46.

* QR code for user invite

Closes #6157.

* Text fix
2024-09-12 12:31:57 +09:00
d11n
3342122be2 Make Role Permissions more human legible (#6191)
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 #6183.
2024-09-12 12:29:10 +09:00
Chukwuleta Tobechi
f59751853a Fx pos data additional info (#6172)
* 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
d11n
a60c55c6df Transactions: Improve TX ID display (#6190)
* Transactions: Improve TX ID display

* Elastic fix

* Test and behaviour fix
2024-09-12 10:08:16 +09:00
d11n
3bad5883bb Permissions: Remove deprecated custodian account policies (#6193)
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
Nicolas Dorier
d4c30866b7 Fix timeout issues during migration (#6208) 2024-09-10 20:11:07 +09:00
Nicolas Dorier
7c92ce771f Reindex Invoices table if corrupt, fix migration timeout (#6207) 2024-09-10 17:34:02 +09:00
nicolas.dorier
4601359ebe Do not block the Lightning PendingPayoutListener if a client is failing 2024-09-10 17:33:36 +09:00
nicolas.dorier
04b1130837 Fix tests 2024-09-09 23:39:59 +09:00
nicolas.dorier
c377617b5a Fix migration issue on invalid json char 2024-09-09 23:15:53 +09:00
nicolas.dorier
222e8f66df Fix entity half migrated entities for v2 2024-09-09 21:21:04 +09:00
nicolas.dorier
87e2f5f414 Add migration logs 2024-09-09 19:03:07 +09:00
Nicolas Dorier
7de05700e9 Edit dictionary should be in JSON format (#6203) 2024-09-09 11:25:36 +09:00
d11n
841f41da2f Invoice: Improve zero amount invoice handling (#6199)
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 #6123.
2024-09-09 11:05:03 +09:00
Dennis Reimann
73dcde7780 UI: Create Store CTA should be full-width
Closes btcpayserver/app#92
2024-09-06 13:23:52 +02:00
Nicolas Dorier
377ff52222 Fix: Incorrect rounding for lightning amounts (#6201) 2024-09-06 18:48:20 +09:00
nicolas.dorier
e4b5609d78 Fix doc 2024-09-06 13:27:12 +09:00
Nicolas Dorier
c93497af10 Rename PaymentMethod => PaymentMethodId (#6198)
* Rename PaymentMethod => PaymentMethodId

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

* Sound improvements

* NFC: Handle no data/empty tag case

Fixes #6154.
2024-09-02 18:20:29 +09:00
nicolas.dorier
9c8fe9277d Fix route's param in doc 2024-09-02 16:27:49 +09:00
nicolas.dorier
a7c70e6912 Add missing doc for scriptPubKeyType TaprootBIP86 2024-09-02 10:35:06 +09:00
Chukwuleta Tobechi
28156a5a6b Include hover to display total sales per day (#6174)
* 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
Nicolas Dorier
a3cfb9e5e4 Removal of the Altcoins build (#6177)
* Remove some useless #if ALTCOINS

* Removal of the Altcoins build
2024-08-30 08:34:23 +09:00
Chukwuleta Tobechi
25ccc6a9f9 Update notificaiton counter record (#6169) 2024-08-29 12:47:29 +02:00
Umar Farooq
8d1904c185 Sales report dashboard issue #6167 - 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 (#6176)
Co-authored-by: Umar Farooq <umar.u.farooq@constellationdealer.com>
2024-08-29 11:30:34 +02:00
nicolas.dorier
85cc221d50 Remove obsolete errors 2024-08-29 10:47:03 +09:00
nicolas.dorier
588b00de45 Fix potential crash in migration 2024-08-29 10:21:25 +09:00
nicolas.dorier
656a1f8294 bump version 2024-08-29 09:40:22 +09:00
Nicolas Dorier
1dd37c5020 Refactor Payouts and PullPayments DB models (#6173) 2024-08-28 18:52:08 +09:00
Nicolas Dorier
3c40dc1f49 Delete columns CustomerEmail, OrderId, ItemCode (#6170) 2024-08-27 09:53:28 +09:00
Nicolas Dorier
6d560caf06 Small optimization of query (#6153) 2024-08-22 18:52:53 +09:00
Nicolas Dorier
07f3301e32 Remove unused code (#6152) 2024-08-22 18:52:41 +09:00
nicolas.dorier
9410a293f7 Fix tests 2024-08-22 17:21:06 +09:00
nicolas.dorier
a0704eddb0 Fix tests 2024-08-22 16:34:37 +09:00
Dennis Reimann
672491c66c UI: Offcanvas fixes 2024-08-20 18:12:32 +02:00
Dennis Reimann
bd91c45d1f Minor design update 2024-08-20 18:12:21 +02:00
rockstardev
f77cdb7148 Removing Strike as BTCPay Foundation supporter and cleaning up old SVGs (#6144) 2024-08-11 13:21:09 -05:00
d11n
7878a4365c Dictionary additions (#6120)
* 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
Nicolas Dorier
94760792af Add parts of the UI to translate (#6119) 2024-07-25 22:46:02 +09:00
Nicolas Dorier
50dafd2452 Uniform primary action id's of pages (#6118) 2024-07-25 08:23:28 +02:00
Nicolas Dorier
ca4abcb497 Allow translations of BTCPay Server Backend by admins (#5662) 2024-07-24 20:16:20 +09:00
napoly
acbc75d077 Fix xmr status message (#6111) 2024-07-19 21:46:24 +09:00
nicolas.dorier
ce4d73fcad Changelog v1.13.5 2024-07-17 11:25:23 +09:00
nicolas.dorier
8b20cd9082 Fix: Plugin Exception Handler didn't disabled plugin if crash was detected 2024-07-17 11:23:14 +09:00
nicolas.dorier
fe01e4693f Fix: Kraken rate provider failing due to bid > ask 2024-07-17 11:10:58 +09:00
nicolas.dorier
1506a2738a Update Changelog 2024-07-15 22:00:07 +09:00
Nicolas Dorier
0eca1ddbf1 Shopify: Cancel rather than close an order (#6108) 2024-07-15 21:57:12 +09:00
Nicolas Dorier
859a8d1a11 Shopify: Create invoice when the payment page opens (#6109) 2024-07-15 21:57:00 +09:00
Nicolas Dorier
8aaf77d4b4 Add changelog and bump 1.13.4 (#6102)
* 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
nicolas.dorier
10977e6750 Move AdditionalData to AppBaseData 2024-07-11 21:43:30 +09:00
nicolas.dorier
66d8dda0f5 Timespan in API should be parsed with invariant culture 2024-07-11 21:41:19 +09:00
d11n
25ae6df095 Greenfield: Add file endpoints and upload (#6075)
* Greenfield: Add file endpoints and upload

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

Closes #6074.

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
d11n
249b991185 Checkout: Display description if present (#6082)
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 #5023.
2024-07-11 00:14:18 +09:00
d11n
bc6d037341 POS: Improve padding on mobile and unify card look with tiles (#6088)
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
d11n
a4a1fa0746 Greenfield: Add store id for notifications (#6093)
* 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
d11n
d73e26e0c4 Fix null pointer exception on receipt print page (#6045) (#6085) 2024-07-11 00:11:04 +09:00
Nicolas Dorier
372688b723 Disable plugins if they crash the Dashboard page (#6099) 2024-07-11 00:09:54 +09:00
d11n
a5ab68ab02 Greenfield: Fix missing delete app policy (#6098) 2024-07-10 22:50:32 +09:00
d11n
e7297ce6b8 Truncate Center Component: Fix Vue rendering (#6087)
* 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
d11n
a88b39e50d Fix missing normalization of Settled invoice status (#6097)
Addition to #5982, which fixes lookups for invoices with status `Settled` (e.g. for Sales tiles on the Dashboard).
2024-07-10 15:33:48 +09:00
Andrew Camilleri
a0988c250d Handle LNURL Payouts failing due to amount restriction (#6061)
* 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
Nicolas Dorier
3a52759090 Bump EF libs (#6096) 2024-07-10 08:50:43 +09:00
d11n
ddb07a7ba9 Receipt page fixes (#6079)
* 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
Dennis Reimann
b5ad5a5f6f TransactionLinkProviders: Don't force single item (#6078)
* TransactionLinkProviders: Don't force single item

Fixes #6077.

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

Fixes #6076.

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

Closes #6067.

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

* Rename PayoutData column to PayoutMethodId
2024-06-28 20:07:53 +09:00
Nicolas Dorier
c56b660c92 bump selenium container (#6071) 2024-06-28 17:32:55 +09:00
Dennis Reimann
6a5bc99a55 Color fix 2024-06-27 16:39:13 +02:00
Dennis Reimann
833895e797 Design system updates; add icons 2024-06-27 10:43:28 +02:00
nicolas.dorier
fa080ce0a4 Add AdditionalData to PointOfSaleBaseData 2024-06-26 17:43:56 +09:00
d11n
2482b9df74 Greenfield: Refactor app endpoints (#6051)
* 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
d11n
bf66b54c9a User: Add name and image URL (#6008)
* User: Add name and image URL

More personalization options, prerequisite for btcpayserver/app#3.

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

* 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
d11n
1ba7b67e70 Add setup script for dev basics like users and stores (#5987)
* 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
d11n
feffbbd980 Greenfield: Manage notifications (#6058)
Prerequisite for btcpayserver/app#12.
2024-06-25 20:01:11 +02:00
napoly
e0c5ac5271 Fix Monero development environment with wallet load (#6066) 2024-06-24 15:16:11 +02:00
Dennis Reimann
50656dc7d3 UI unifications 2024-06-23 19:38:34 +02:00
nicolas.dorier
cbeea86a13 Do not crash the plugin packer if Assembly.GetTypes crashes 2024-06-21 10:16:16 +09:00
d11n
5a6bdd756a POS: Keypad error should sit below the header (#6055)
Fixes #6054.
2024-06-20 16:17:36 +02:00
d11n
cf7bb8cff7 Fix tests (#6060)
* docker-compose: Add missing restart for merchant CLN container

* Upgrade ChromeDriver

* Fix path and test
2024-06-20 10:48:23 +02:00
d11n
b898fd0ec1 TimeSpan JSON Converter: Parse strings (#6012)
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
d11n
f8f98ab7f0 BTCPayServerClient refactoring (#6024)
* 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
d11n
0f8da123b8 UI: Move section navigation to sidebar (#5744)
* 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
napoly
aa9e1acb91 Fix Monero local dev docker start up (#6042) 2024-06-18 09:57:54 +02:00
Kukks
61e4995c14 bump v 2024-06-13 14:49:36 +02:00
d11n
078ce28b8a v1.13.3: Add Changelog (#6048)
* v1.13.3: Add Changelog

* Update Changelog.md

---------

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

Closes #5999.
2024-06-05 22:11:26 +09:00
Kukks
b5ade89763 fix shopify 2024-06-05 08:53:40 +02:00
Andrew Camilleri
2c63d16774 Refactor shopify logic (#6029)
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
nicolas.dorier
36fcde29ae Update changelog 2024-06-03 22:03:35 +09:00
Andrew Camilleri
c5aca1b7f7 Cancel shopify order when invoice payment fails (#6027)
* Cancel shopify order when invoice payment fails

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

* Add and fix test
2024-05-23 20:22:16 +09:00
d11n
89fcf86bb4 POS: Allow overpay for articles with minimum price (#5997)
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 #5995.
2024-05-23 20:20:58 +09:00
nicolas.dorier
faf6b514e6 Fix error message if rate unavailable 2024-05-21 22:11:03 +09:00
Nicolas Dorier
7564c3c2bd Fix: Some valid taproot PSBT couldn't parsed and show better error message (Fix #5715) (#5993) 2024-05-21 10:52:55 +09:00
d11n
fc9d4f96a7 Design system and icon updates for 2.0 (#5938)
* 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
nicolas.dorier
c6d46fcdd0 Minor refactorings 2024-05-17 14:48:55 +09:00
nicolas.dorier
8331d1019f Can hide the footer of LayoutSimple 2024-05-17 09:28:00 +09:00
nicolas.dorier
900c5f7976 Show boltcard deeplink also on iOS 2024-05-17 09:10:29 +09:00
rockstardev
d2a04db49f Cutting off lightning payment so receipt takes less space 2024-05-16 09:41:28 +09:00
Nicolas Dorier
7793c5e5df Allow user to input a passphrase for Trezor v1 (Fix #5794) (#5980) 2024-05-15 09:18:20 +09:00
Nicolas Dorier
69e0ae76c7 Fix: Error while connecting to websockets without reverse proxy (#5981) 2024-05-15 09:17:45 +09:00
Nicolas Dorier
c134602cbd Remove Legacy Status from the code (#5982) 2024-05-15 07:49:53 +09:00
Nicolas Dorier
d96b066658 Recommended exchange to be resolved during Invoice Creation (#5976)
* Recommended Exchange Rate Selection during Invoice Creation

* Make Recommended exchanges pluginifiable
2024-05-13 22:29:42 +09:00
nicolas.dorier
b0da802abe Fix broken build 2024-05-10 22:18:57 +09:00
nicolas.dorier
2c4d87aef8 Fix migration crash on postgres above 13.10 2024-05-10 22:13:08 +09:00
Chukwuleta Tobechi
11b38a7a4c allow admin update default rate provider and default currency for stores (#5918)
* 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
nicolas.dorier
84df96429c Refactor of Default RateRules 2024-05-10 08:55:53 +09:00
Nicolas Dorier
adbe5977cd Decouple DefaultRates from BTCPayNetwork (#5974) 2024-05-09 17:20:24 +09:00
d11n
4c303d358b Branding updates for 2.0 (#5947)
* Remove deprecated CSS options

Closes #5945.

* Greenfield: Add brandColor to store APIs

Closes #5946.

* Migrate file IDs to URLs

Closes #5953.

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

Closes #5945.

* 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
d11n
eba3475a1b Theme docs link fix (#5972)
We recently removed the section the anchor links to and we'll remove the links entirely in #5947.
2024-05-08 09:11:04 +09:00
Nicolas Dorier
8bb4ceaaac Remove dead code from multi db support (#5971) 2024-05-07 09:21:49 +09:00
Nicolas Dorier
a89c0d4797 Add refund reports (#5791)
* Add refund reports

* Fix fake data generator in reports
2024-05-06 18:44:16 +09:00
d11n
a8e16b0ba6 Wallet UI quick wins (#5851)
- Unify single/multiple inputs display
- Destination: Move labels above amount
- Coin Selection: Add sorting by amount and confirmations, add page size. Closes #5850
- 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
Nicolas Dorier
7de4e8b001 Show better error message for invalid destination in PullPayments (#5969) 2024-05-05 21:03:25 +02:00
Nicolas Dorier
b4cd74056e Remove period concept from PullPayment (#5963) 2024-05-01 17:59:10 +09:00
Nicolas Dorier
9db9c5e936 Decouple PaymentMethodId from PayoutMethodId (#5944) 2024-05-01 10:22:07 +09:00
nicolas.dorier
247afe6a7b Fix launchsettings 2024-04-30 20:12:11 +09:00
nicolas.dorier
90bc087ad6 Fix test 2024-04-30 18:41:08 +09:00
Andrew Camilleri
6049fa23a7 Support pluginable rate providers (#5777)
* 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
Andrew Camilleri
4821f77304 Guard against running current master (#5959)
* 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
d11n
c348f442cc Checkout: Fix copying numeric values to clipboard (#5962)
Fixes #5960.
2024-04-30 18:26:27 +09:00
ndeet
c23fab5b34 Greenfield API clarifications, fix typo subscriptions -> registrations (#5955) 2024-04-30 18:22:41 +09:00
d11n
8d429f064b Show Lightning node availability in navigation (#5951)
* 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  #5940.

* 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
Nicolas Dorier
d3277306cf Avoid timeouts during EF migrations (#5937) 2024-04-25 17:27:45 +09:00
nicolas.dorier
4e0423cb1e Disable mempool fee test 2024-04-25 14:16:03 +09:00
Nicolas Dorier
0f08d3e3a3 Remove migrations prior to 1.0.3.162 (#5939)
* Consolidate EF migrations up to 03/2020 into a single SQL script

* Remove old migrations code
2024-04-25 14:09:21 +09:00
Nicolas Dorier
0c35939001 Cleanup data from the InvoiceEvents table (#5904) 2024-04-25 14:09:01 +09:00
d11n
06edb0e157 Server email settings: Fix missing password field (#5952)
Fixes #5949.
2024-04-24 23:02:54 +09:00
nicolas.dorier
c0f9716b1e bump NTag424 lib 2024-04-24 21:50:18 +09:00
d11n
d10e07b67b Pull payment QR scan fixes (#5950)
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
nicolas.dorier
22cf253183 Remove some legacy code 2024-04-24 17:24:15 +09:00
d11n
56d57bbd84 Improve data display on receipt (#5896)
Once more an improvement for the receipt, which also fixes #5882:

- 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
d11n
aeb836da76 Domain mapping constraint: Fix .onion case (#5948)
Fixes #5917, which is a regression introduced in #5776. 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
nicolas.dorier
e4440ca0dc Fix node public info 2024-04-24 10:14:11 +09:00
Andrew Camilleri
8c4cf779ce fix bolt 11 0 amount check for payouts (#5943)
Co-authored-by: d11n <mail@dennisreimann.de>
2024-04-23 09:36:49 +02:00
nicolas.dorier
c8f003950a Remove debug logs 2024-04-22 10:54:44 +09:00
Nicolas Dorier
f4aafd5be3 Cleanup AddressesInvoices table (#5905) 2024-04-16 16:18:56 +09:00
Nicolas Dorier
bd978f29a0 Fix flaky tests (#5934) 2024-04-16 12:48:42 +09:00
d11n
76719cdc4a Greenfield: Fix payment method update regression (#5932)
* Greenfield: Fix payment method update regression

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

* Fix PaymentMethodId Swagger docs
2024-04-16 11:32:52 +09:00
nicolas.dorier
36cfc3648f bump 2024-04-15 22:26:50 +09:00
d11n
7e80fd2e98 Prevent payout double send (#5931)
Fixes #5913.
2024-04-15 13:25:17 +02:00
Nicolas Dorier
f1a04a3bd0 Remove MySQL and Sqlite deps (#5910) 2024-04-15 19:08:25 +09:00
Nicolas Dorier
9699b3837b Bump CLightning (#5923) 2024-04-15 18:22:30 +09:00
Chukwuleta Tobechi
d2e95c2246 update csv export to include full date and time in 12 hour format (#5922)
* 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
nicolas.dorier
893b92439c Fix: Unable to use a different postgres schema (Fix #5901) 2024-04-15 11:38:32 +09:00
Andrew Camilleri
8819372d2e Small payment request fixes (#5926)
* Do not crash payment request page on 0 amount

* set email from form to payment request
2024-04-12 12:56:11 +02:00
Henry Hollingworth
b9cea968e5 (bug) handle null XMR settings (#5909)
Co-authored-by: Henry Hollingworth <henry.hollingworth@alcoa.com>
2024-04-12 07:51:56 +02:00
nicolas.dorier
6846a7cef5 Fix JSContent test 2024-04-08 21:32:55 +09:00
Nicolas Dorier
eee3fb8b5f Merge pull request #5906 from NicolasDorier/fweohtqn
Remove CheckoutV1
2024-04-06 11:55:36 +09:00
Dennis Reimann
6245ecc8f4 Fix tests, remove duplicate tests 2024-04-05 18:56:16 +02:00
Dennis Reimann
d2e9ec9494 Cleanup v2 leftovers 2024-04-05 18:05:51 +02:00
nicolas.dorier
4208110d57 Remove CheckoutV1 2024-04-05 16:58:13 +09:00
Nicolas Dorier
ff5e03a8b1 Merge pull request #5902 from btcpayserver/fweoinqrnt
Fix migration crash
2024-04-04 23:11:21 +09:00
nicolas.dorier
57851cef9a Fix migration crash 2024-04-04 23:10:58 +09:00
Nicolas Dorier
e7cb3c0a85 Merge pull request #5900 from dennisreimann/store-controller-refactoring
Refactoring UIStoresController
2024-04-04 18:21:02 +09:00
Dennis Reimann
620ebc751c Convert to file-scoped namespace 2024-04-04 11:00:18 +02:00
Dennis Reimann
c7a8523b77 Code cleanups 2024-04-04 10:58:47 +02:00
Dennis Reimann
df4d370524 Rename and clean up properties 2024-04-04 10:48:09 +02:00
Dennis Reimann
71ba5d9c4c Move actions and methods to separate partial controllers 2024-04-04 10:48:07 +02:00
Jabster28
d216ad7da9 fix: switch to using get_info for monerod (#5885) 2024-04-04 17:38:41 +09:00
Nicolas Dorier
6cc1751924 The Big Cleanup: Refactor BTCPay internals (#5809) 2024-04-04 16:31:04 +09:00
rockstardev
69b589a401 Adding Tether as BTCPay Server Foundation Supporter (#5891)
* 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
nicolas.dorier
db73b1f268 Fix test CheckJSContent 2024-04-01 12:11:00 +09:00
Snoppy
9ac0e982d6 chore: fix typos (#5883) 2024-03-30 10:20:24 +01:00
Andrew Camilleri
cb25c225e9 Remove custodians (#5863)
* 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
d11n
5b31d4de20 v1.13: Update changelog (#5879) 2024-03-28 22:40:18 +09:00
d11n
14f8c73b08 POS: Increase size of quantity buttons (#5877)
Closes #5873.
2024-03-28 09:01:56 +09:00
d11n
529075f64c Make "Employee" default role on store settings (#5874)
* Refactoring: Use property rather than injecting StoreRepository

* Update info text

* Make "Employee" default role on store settings

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

Closes #5866.

* Obfuscate contact email on public pages

Closes #5870.

* Fix
2024-03-27 19:19:39 +09:00
ndeet
83028b9b73 Adding introduction, Authentication and Usage examples sections to the API docs. (#5858) 2024-03-24 00:02:01 +09:00
d11n
1fe766cb16 Keypad: Fix images (#5857) 2024-03-22 15:16:59 +01:00
Andrew Camilleri
6b45eb0d3d Do not throw when local node is not synced and using external ln node (#5859)
* 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
nicolas.dorier
6b0087ab69 Update version 2024-03-21 21:40:09 +09:00
d11n
e60fd8d6ab Changelog v1.13 (#5840) 2024-03-21 21:39:06 +09:00
Andrew Camilleri
88a1d83323 Support bbqr psbts (#5852)
* Support bbqr psbts

https://bbqr.org/ @nvk

* add js test for bbqr
2024-03-21 10:30:23 +01:00
Andrew Camilleri
e21a8df0f3 smaller printed receipts (#5856) 2024-03-21 17:30:34 +09:00
d11n
93f37b506b UI: Improve Create First Store view (#5854)
Unifies the width and display with the login view.
2024-03-21 07:37:15 +01:00
Chukwuleta Tobechi
fca3480e37 Specify mailto: prefix for emails in Server Settings (#5844)
* 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
d11n
966547db54 Template Editor: Apply item changes directly (#5849)
Closes #5847.
2024-03-19 14:59:26 +01:00
d11n
09dbe44bca Onboarding: Invite new users on store level (#5719)
* 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 #5726.

* Add permissioned form tag helper

* Better way of changing a user's role

* Test fixes
2024-03-19 14:58:33 +01:00
rockstardev
b7ce6b7400 Providing additional parameter for info message (#5756)
* 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
soonsouth
78f169cd24 chore: remove repetitive words (#5842)
chore: remove repetitive words

Signed-off-by: soonsouth <cuibuwei@163.com>
2024-03-18 10:49:07 +01:00
Chukwuleta Tobechi
d0e11f1ec4 changing check box to toggle in various setting views (#5769)
* 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
Nicolas Dorier
912a706de9 Make Payouts and PullPayments columns JSONB (#5800) 2024-03-14 11:13:26 +01:00
d11n
9b5c8a8254 POS: Add item list to keypad (#5814)
* 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
Nicolas Dorier
e5adc630af If pull payment opened in mobile, use deeplink to setup card (#5613)
* 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
Henry Hollingworth
c56c6401d6 (feat) monero settlement thresholds (#5807)
* (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
Andrew Camilleri
0e64df3bbf Parallel payout ln (#5781) 2024-03-14 10:29:14 +01:00
Andrew Camilleri
e497903bf4 Support Admin being able to view stores (#5782)
* Support Admin being able to view stores

* fix null check

* Delete obsolete empty view

* Add test

* Apply CanViewStoreSettings policy changes

Taken from #5719

* Fix Selenium tests

* Update dashboard permission requirement

---------

Co-authored-by: Dennis Reimann <mail@dennisreimann.de>
2024-03-14 10:25:40 +01:00
Chukwuleta Tobechi
f1ff913cbe PoS app to show POS view for easy setup (#5825)
* 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
nicolas.dorier
3a00d32ce0 Fix flaky 2024-03-13 22:51:55 +09:00
nicolas.dorier
f0f698f411 Fix tests (Fix #5831) 2024-03-13 22:29:39 +09:00
d11n
22c6468a5d Wallet: Label filter dropdown (#5802)
Uses a dropdown component for the label filter, which also work on mobile. Closes #5722.
2024-03-12 10:48:37 +01:00
Andrew Camilleri
a60072a431 do not have report name conflict with old plugin (#5826)
* do not have report name conflict with old plugin

* tryadd instead of add

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

Successor of #5762 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
nicolas.dorier
3b73d5a5cb bump client version 2024-03-08 19:56:04 +09:00
Nicolas Dorier
1fd3054006 Fix: Old payments not showing up in reports (#5812) 2024-03-05 16:10:54 +09:00
Fawaz Ahmed
2db1434929 Fix currency-api link (#5803) 2024-03-04 10:10:30 +09:00
Arvin
a171671fe5 Update .NET 8.0 in vscode launch.json (#5805) 2024-03-01 22:07:34 +01:00
d11n
9160a1d71e Store Logo: Remove restriction of square dimension (#5738)
As discussed on #5718, 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
d11n
a896560a3c Lightning Setup page fixes (#5796)
* Lightning Setup: Fix missing headline

Fixes #5795.

* Lightning Setup: Fix tab switching UI glitch

Fixes #5778.
2024-02-29 06:56:34 +01:00
d11n
e43b4ed540 Onboarding: Invite new users (#5714)
* 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
Chukwuleta Tobechi
8b446e2791 Reposition the camera scan icon in the wallet > send functionality (#5790)
* 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
Pavlenex
d72b0e4cee Merge pull request #5610 from NicolasDorier/warnignremov
Remove disclaimer for lightning being in experimentation
2024-02-24 21:33:26 +05:00
d11n
22996ea21e UI: Deprecate the custom CSS options (#5735)
* 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
d11n
d55770cc16 Admin overview of the stores on the instance (#5745)
* Admin overview of the stores on the instance

POC/Draft for #5674.

* 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
Andrew Camilleri
5c98ca180a Webhook tests + FIXES + DOCS (#5686)
* 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
nicolas.dorier
10bb75ce0e Add debug flaky test statement 2024-02-22 19:08:01 +09:00
d11n
b9e3686fcf Fix build warnings and flaky tests (#5780)
* 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
d11n
4ae1046571 Server Settings: Customize instance name and add contact URL (#5718)
* Server Settings: Customize instance name and add contact URL

- The custom instance name would improve #5563
- Added contact URL closes #4806

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

* Remove unused sanitizer from apps

* Allow mailto: links

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

Fixes #5747.

* Fix empty user info case
2024-02-21 14:45:05 +01:00
Andrew Camilleri
f72a6df55a Add legacy report (#5740) 2024-02-21 14:44:49 +01:00
d11n
9c95b98f3a Policies: Cleanup and improvements (#5731)
* 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
ndeet
55a8ba0905 Adding link to API usage examples in docs. (#5772) 2024-02-21 14:42:15 +01:00
Nisaba
04037b3d2d Crowdfund : Add Buyer information / Additional information(forms) like POS (#5659)
* 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
d11n
4943c84655 Invoice: Improve events display (#5775)
Closes #5773.

- 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
d11n
42a8160768 UI: Make store selector list scrollable if necessary (#5760)
Fixes #5754.
2024-02-21 13:34:47 +01:00
d11n
33d3a25928 Apps: Don't redirect .onion requests to canonical domain (#5776)
Fixes #5729.
2024-02-21 13:34:12 +01:00
Nicolas Dorier
c2acff81c6 Fix: Labels wouldn't be properly applied to some wallet's transactions (#5770) 2024-02-20 18:42:38 +09:00
Kukks
214d4b0c3f Support 16mb psbts. Potentially fixes #5768 2024-02-19 14:14:41 +01:00
Kukks
bd4cf61c2b potentially fix #5764 2024-02-19 13:01:08 +01:00
rockstardev
b592ee2fed Bumping LND to 0.17.4-beta (#5739)
* 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
d11n
c57e1cca25 Shopify: Improve instruction display (#5752) 2024-02-16 09:36:41 +01:00
BitcoinMitchell
335f345ce3 Update GeneralSettings.cshtml (#5748)
Removed trailing data
2024-02-13 09:48:16 +01:00
nicolas.dorier
b7be93c569 Update NTag424 lib 2024-02-08 19:12:14 +09:00
nicolas.dorier
cd01a7b727 Improve performance of payout db queries 2024-02-08 16:44:03 +09:00
nicolas.dorier
b96e73a002 Fix: Payouts state could turn cancelled even if payment was successful 2024-02-08 16:32:41 +09:00
d11n
0bf22ddf29 Do not require user approval by default (#5733)
As discussed on Mattermost.
2024-02-06 17:04:18 +09:00
Pavlenex
1c4dc382a8 Merge pull request #5683 from pavlenex/release-cycles-doc
Create RELEASE-CYCLES.md
2024-02-05 19:42:15 +05:00
d11n
71c5566f2b Dashboard: Tooltip for balance on a particular day (#5650)
Closes #5617.
2024-02-02 11:29:35 +01:00
Chukwuleta Tobechi
6621859567 remove decimals for Colombian (COP) and Argentina's Peso (ARS) (#5710)
* 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
Nicolas Dorier
6437967e60 Fix: Closing Balance in Dashboard was showing incorrect value (#5716) 2024-02-01 15:13:05 +09:00
nicolas.dorier
c5a926c50c Fix Kraken rate for LTC 2024-02-01 14:45:59 +09:00
nicolas.dorier
85ab691b68 bump version 2024-02-01 14:17:14 +09:00
nicolas.dorier
4d3e0ab599 Changelog 2024-02-01 10:13:18 +09:00
nicolas.dorier
02663a149e Fix Kraken API 2024-02-01 10:09:32 +09:00
Chukwuleta Tobechi
a8fdc4798d Remove randomize RBF from wallet UI advanced settings (#5709)
* 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
d11n
6290b0f3bf Admins can approve registered users (#5647)
* 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 #5656.

* 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
Chukwuleta Tobechi
411e0334d0 Bitnob rate provider (#5705)
* Bitnob rate provider

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

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

* Split email and notification settings
2024-01-26 10:28:50 +01:00
nicolas.dorier
2111b67e2c Update changelog 2024-01-25 21:03:27 +09:00
d11n
b96cfcd14d Apps: Allow authenticated, non-owner users permissioned access (#5702)
Fixes #5698. 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
d11n
086f713752 Wizard UI: Constrain navigation width (#5697)
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 #5693.
2024-01-25 16:38:05 +09:00
Nicolas Dorier
fd67e09cf0 In Wallet Send, label were not applied to transactions (#5700) 2024-01-25 16:37:49 +09:00
nicolas.dorier
6f4ca47532 Add documentation for wallet export on sparrow 2024-01-25 16:37:15 +09:00
nicolas.dorier
f97f23c8a5 Do not dispose connections created by EF 2024-01-25 10:45:02 +09:00
nicolas.dorier
b62985faf4 Update changelog 2024-01-24 22:55:23 +09:00
nicolas.dorier
09c761aa31 Fix: Sometimes importing a wallet file from Electrum would fail 2024-01-24 22:53:40 +09:00
d11n
8089a938f3 Guest role: Fix redirect after store creation (#5689)
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 #5688.
2024-01-24 11:34:16 +01:00
Nicolas Dorier
35b3fef7c5 Fix wallet import (#5695)
* Fix wallet import

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

* Re-add Electrum distinction

* Specter: Fix indentation

* Cleanups
2024-01-24 09:28:22 +09:00
Nicolas Dorier
b03f8db06b Refactor wallet file parsing (Fix: #5690) (#5692) 2024-01-23 21:33:45 +09:00
nicolas.dorier
27e70a169e Do not show warning about browser compatibility to vault on confirm address 2024-01-23 21:30:29 +09:00
d11n
6a1d17dda2 Remove ESC as a supporter (#5685)
Closes #5684.
2024-01-22 09:00:10 +09:00
Pavlenex
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
nicolas.dorier
31bc6dd48c More tests on interpolation 2024-01-20 12:21:58 +09:00
Nicolas Dorier
6054315d84 Add changelog 1.12.4, bump (#5678)
* Add changelog 1.12.4, bump

* Update Changelog.md

Co-authored-by: d11n <mail@dennisreimann.de>

* Update Changelog.md

---------

Co-authored-by: d11n <mail@dennisreimann.de>
2024-01-19 23:28:01 +09:00
Nicolas Dorier
2a7059ddeb Update languages updates from transifex (#5679)
* Update languages

* Update ChatGPT translator script

* Update translations
2024-01-19 21:45:14 +09:00
Andrew Camilleri
e2e7e59722 Fix webhook test for payment requests (#5680)
When testing the webhook for payment requests, we were incorrectly creating a payout webhook instead of a payment request. This would cause an error (but nothing fatal as it is only a test webhook(
2024-01-19 21:30:15 +09:00
nicolas.dorier
8b373bda8e bump NBX 2024-01-18 17:21:15 +09:00
Nicolas Dorier
d6806dc1f6 Improve checkout page load time by fetching recommended fee in the background periodically (#5672) 2024-01-18 17:16:57 +09:00
Andrew Camilleri
a753698ae7 Various plugin fixes (#5577)
* Fix: Plugin updates do not work

* Offer install on disabled plugins when different version

This will:
* Clear any previous pending actions of a plugin if you click uninstall
* Show the plugin version that was disabled
* Show an update button on disabled plugins instead of install
* if a plugin is scheduled to be installed/updated, it will show which version was scheduled to be updated. If a newer version if available than the scheduled one, it will show an option to switch to that

* Ensure disabled plugins don't get loaded

* View fixes

---------

Co-authored-by: d11n <mail@dennisreimann.de>
2024-01-18 17:15:16 +09:00
Andrew Camilleri
3eec9cb0bb Refactor fee provider (#5643)
* Refactor fee provider

The fee provider ended up glued with a hardcoded factory. This PR:
* removes this glue and uses the DI to register fee provider for a network. (allows plugins to add their own fee providers, for any network
* Add a 10 second timeout to mempoolspace fee fetching as they are slow at times

* use linear interpolation for mempool space fee estimation

* fix upper bound

* Add tests, rollback pluginify FeeProvider

---------

Co-authored-by: nicolas.dorier <nicolas.dorier@gmail.com>
2024-01-18 15:27:19 +09:00
Nicolas Dorier
cd8ef0c1ff Fix: Bitpay's API rate route wasn't backward for some queries (#5671) 2024-01-18 14:08:07 +09:00
nicolas.dorier
bd196ad963 fix build 2024-01-18 12:31:59 +09:00
nicolas.dorier
1ad93838c9 Remove reliance on static field 2024-01-18 11:13:32 +09:00
Nicolas Dorier
a9252fd741 Fix: Partial Payment shows 'Could not update BTC (LNURL-Pay)' in invoice logs (#5670) 2024-01-18 09:57:25 +09:00
Nicolas Dorier
376067324b Remove unused variables (#5669) 2024-01-18 09:47:39 +09:00
Nicolas Dorier
dd7ab2f647 Avoid exception storm when currency provider is initialized (#5668) 2024-01-18 09:31:35 +09:00
Nicolas Dorier
1d6d146fb2 Revert "Remove unused variables" (#5667)
This reverts commit f070b22355.
2024-01-18 00:05:50 +09:00
nicolas.dorier
3ae1f13323 Bump libraries 2024-01-17 22:11:30 +09:00
nicolas.dorier
0b0a8f8218 Fix: BTCPay Server fails to start the first time when installing a new plugin (#5595) 2024-01-17 19:26:22 +09:00
nicolas.dorier
f070b22355 Remove unused variables 2024-01-17 18:46:28 +09:00
Andrew Camilleri
c5a0e28420 Refactor Wallet import code (#5638)
* Refactor Wallet import code

The code for wallet import was incredibly messy as it evolved over time from various requests.

This PR:
* splits up each supported format into its own file
* Supports taproot descriptors (through a hack until NBitcoin supports it internally) fixes #5518
* Reduces different paths for handling electrum/non-electrum xpubs
* Allows plugins to add their own import support formats for onchain wallets.

* Update NBitcoin to parse tr descriptors

* Fix warnings

* Use dedicated type OnChainWalletParsers

---------

Co-authored-by: nicolas.dorier <nicolas.dorier@gmail.com>
2024-01-17 18:08:39 +09:00
d11n
70e9ea1d5e POS: Fix missing store branding property on form error case (#5658)
When a POS has a form, which results in an error state, the store branding property was not set. This adds the missing property and also does not render the store branding partial, in case the model property isn't present.

Fixes #5655.
2024-01-16 08:55:38 +01:00
d11n
89d294524a Checkout v2: Clicking QR code copies full payment URI (#5627)
* Checkout v2: Clicking QR code copies full payment URI

Before it copied only the destination value (Bitcoin address or Lightning BOLT11). This didn't include the BOLT11 in case of the unified QR code. Now it will copy the full payment URI, which is the same as the QR represents:

- Unified: `bitcoin:ADDRESS?amount=AMOUNT&lightning=BOLT11`
- Bitcoin: `bitcoin:ADDRESS?amount=AMOUNT`
- Lightning: `lightning:BOLT11`

Fixes #5625.

* Test fix
2024-01-16 08:54:59 +01:00
d11n
5e25ee2996 Checkout v1: Apply custom style (#5628)
Applies the custom CSS in Checkout v1 and prevents that it interferes with the styling of Checkout v2.

Fixes #5615 and fixes #5616.
2024-01-15 13:30:39 +01:00
d11n
5935dbf1d1 Store Emails: Fix test email with multiple recipients (#5649)
Fixes #5648.
2024-01-15 13:30:10 +01:00
Chukwuleta Tobechi
f7542c988d Prevent payment request to be created when a wallet is not set up (#5620)
* Prevent payment request to be created when a wallet is not set up

* Created an extension method for store wallet checks

* fix for invoice and payment request selenium test

* refactoring payment request controller

* removing unused variable

* Unify behaviour across controllers

---------

Co-authored-by: Dennis Reimann <mail@dennisreimann.de>
2024-01-11 16:25:56 +01:00
Andrew Camilleri
e90414bded Hide LN Balance when using internal node and not server admin (#5639)
* Hide LN Balance when using internal node and not server admin

* Minor updates

---------

Co-authored-by: Dennis Reimann <mail@dennisreimann.de>
2024-01-06 08:46:19 +01:00
Luan Pham
78882dcff0 Propose linking Greenfield API information within the Legacy API view (#5635)
* Propose linking Greenfield API information within the Legacy API view

* Propose linking Greenfield API information within the Legacy API view

* moved Greenfield API section up

* moved Greenfield API section up

* Fix link

* Wording

* Adjust button alignment

---------

Co-authored-by: Dennis Reimann <mail@dennisreimann.de>
2024-01-03 11:33:54 +01:00
Andres G. Aragoneses
1ac1443070 README.md: .NET is not called "Core" anymore (#5636)
* README.md: .NET is not called "Core" anymore

Ever since version 5.x, the "Core" part of the name was removed.

* README.md: remove unneeded lang setting from URL
2024-01-02 12:29:40 +01:00
Chukwuleta Tobechi
b5405e9313 Make tips and discount properties disabled in POS setting (#5619)
* Make tips and discount properties disabled in POS setting

* Update discount and tips boolean properties in model and swagger json

* update pos tests to cater for default tip and discount state

* Remove custom IDs and unify tests

---------

Co-authored-by: Dennis Reimann <mail@dennisreimann.de>
2023-12-31 09:07:15 +01:00
Luan Pham
c7eef01fd5 Removed what's new button and info #5608 (#5618) 2023-12-28 08:57:18 +01:00
rockstardev
26f61d35bb Bumping LND to 0.17.3-beta (#5614) 2023-12-25 00:27:46 -06:00
Ryan Heneise
765776c429 Update .NET version in README.md (#5609)
Update the version of .NET requirement from 6.0 to 8.0 in README.md
2023-12-24 15:40:38 +01:00
nicolas.dorier
f9a43b537f Remove disclaimer for lightning being in experimentation 2023-12-24 11:12:51 +09:00
d11n
9f54074d03 Startup: List configured networks in non-altcoin build warning (#5593) 2023-12-22 17:25:04 +09:00
Nicolas Dorier
f23078df1c Use buildx for creating and pushing docker images (#5592) 2023-12-22 14:23:04 +09:00
nicolas.dorier
a35bf54a02 Changelog and bump 2023-12-22 14:21:12 +09:00
d11n
4867698ac9 AppService: Update inventory only for known app types (#5590)
There are apps, which do not have a template and hence no inventory. Accessing it via `settings[templatePath]!.Value` causes exceptions in those cases.
2023-12-22 14:21:01 +09:00
nicolas.dorier
e84e575017 bump 2023-12-22 10:53:54 +09:00
d11n
c585a0b276 Webhooks: Fix invoice interpolation (#5586)
* Webhooks: Fix invoice interpolation

Fixes #5584.

* Syntax cleanups
2023-12-22 10:50:08 +09:00
d11n
ad89139e07 Plugins: Fix missing uninstall button (#5587)
Fixes #5585.
2023-12-22 10:49:40 +09:00
Nicolas Dorier
ebc053aca5 Update Changelog (#5583) 2023-12-21 23:46:29 +09:00
d11n
96da7f0322 UI: Form validation summary matches alert style (#5576)
Fixes #5564.
2023-12-21 23:43:12 +09:00
d11n
8ae9e59d9d Lightning Address: Use lowercased username when resolving (#5579)
* Lightning Address: Use lowercased username when resolving

* Use static NormalizeUsername
2023-12-21 23:42:17 +09:00
nicolas.dorier
c94dc87cb8 Fix: Setup a boltcard for the second time wouldn't generate new keys 2023-12-21 18:16:25 +09:00
nicolas.dorier
20512a59b3 Fix API doc for boltcard related feature 2023-12-21 18:02:13 +09:00
Nicolas Dorier
b3f9216c54 Use PullPaymentId to derive the cardkey of Boltcard (#5575) 2023-12-21 10:29:28 +09:00
nicolas.dorier
1cda0360e9 Fix test 2023-12-20 22:00:08 +09:00
nicolas.dorier
7f75117bfa Fix flaky 2023-12-20 20:59:27 +09:00
d11n
5a70345499 Do not redirect to archived store after login (#5566)
Now that we have archived stores, we need to exclude them from the selection of the default store the user gets redirected to after login.
2023-12-20 19:27:02 +09:00
d11n
5114a3a2ea Lightning: Fix connection display name in LN settings (#5569)
* Lightning: Fix connection display name in LN settings

Builds on btcpayserver/BTCPayServer.Lightning#153.

* Upgrade Lightning lib
2023-12-20 19:26:24 +09:00
d11n
93ab219124 Lightning: Allow LND to be used with non-admin macaroons (#5567)
* Lightning: Allow LND to be used with non-admin macaroons

Requires btcpayserver/BTCPayServer.Lightning#152.

* Upgrade Lightning lib
2023-12-20 19:23:46 +09:00
Andrew Camilleri
61bf6d33b2 Handle disabled plugin in ui (#5570)
When a plugin is disabled, we should at least show the uninstall option in the plugin option. Eventually we should also detect what version was disabled and offer an update instead
2023-12-20 18:56:21 +09:00
Nicolas Dorier
3fc687a2d4 Fix: Payments to Top-Up could be undetected due to race condition (#5568) 2023-12-20 18:41:28 +09:00
nicolas.dorier
8da04fd7e2 Better error message in Vault if hardware device isn't supported 2023-12-20 17:17:19 +09:00
nicolas.dorier
cb54f8f6d1 Avoid updating Apps if no inventory has been modified 2023-12-19 21:48:11 +09:00
Kukks
6ecfe073e7 disable cj plugin on next btcpay release 2023-12-19 12:58:52 +01:00
Nicolas Dorier
ea2648f08f Fix: Update of inventory could override app settings being updated (#5565) 2023-12-19 20:53:11 +09:00
nicolas.dorier
40adf7acd2 Add flaky test debug statements 2023-12-19 13:55:33 +09:00
nicolas.dorier
850af216bd Add debug statments in flaky tests 2023-12-19 13:00:48 +09:00
d11n
bf6200d55c Changelog v1.12 (#5528) 2023-12-19 12:39:23 +09:00
nicolas.dorier
93bb85ffaa Fix tests 2023-12-19 12:35:35 +09:00
nicolas.dorier
2fa7745886 Select 1 hour as default fee rate 2023-12-19 12:23:20 +09:00
nicolas.dorier
2714907aef Improve exception message if Bitpay rates are unavailable 2023-12-19 11:44:10 +09:00
nicolas.dorier
0d61e45cc6 Increase absurdfee from mempool space 2023-12-17 11:54:56 +09:00
Andrew Camilleri
541cef55b8 Random feerate and ensure sanity (#5556)
Suggested at https://github.com/btcpayserver/btcpayserver/pull/5490#issuecomment-1851066223
We can also configure this httpclient to use tor
2023-12-14 21:20:45 +09:00
d11n
e3863ac076 Allow users with CanViewPaymentRequests to view payment requests (#5551) 2023-12-14 12:42:07 +01:00
d11n
0e2379caa6 Plugins: Add disclaimer (#5552) 2023-12-14 12:41:37 +01:00
d11n
a17c486f81 POS: Remove forced center alignment for description (#5555)
Allows to specify the text alignment in the description container via the richt text editor. Before it was center aligned, no matter what one did in the editor.

This is feedback we got in yesterdays call with Start9.
2023-12-14 12:09:45 +01:00
Andrew Camilleri
e4aaff5e34 Greenfield: Fix invoice refund permission (#5558) 2023-12-14 11:15:36 +01:00
Kukks
97fda9d362 not lndhub specific 2023-12-13 13:40:18 +01:00
Andrew Camilleri
7a06423bc7 Allow scheduling installs/updates of future plugins (#5537)
Co-authored-by: Dennis Reimann <mail@dennisreimann.de>
2023-12-13 12:36:23 +01:00
d11n
26374ef476 Policies: Add warnings for certain options (#5554) 2023-12-13 10:53:37 +01:00
Andrew Camilleri
6324a1a1e8 Remove bittrex (#5553)
* Remove bittrex

* Test fix

---------

Co-authored-by: Dennis Reimann <mail@dennisreimann.de>
2023-12-12 17:38:28 +01:00
Kukks
b751e23e93 dont crash if the plugin builder provides more instances of the same plugin but different v 2023-12-12 13:23:33 +01:00
nicolas.dorier
72ee65843d bump bitcoin core 2023-12-12 13:08:40 +09:00
Nicolas Dorier
d413dd9257 UI: Improve invoice's webhooks table (#5545) 2023-12-11 14:45:45 +09:00
Kukks
433adf4668 Fix Email rules validation and command index 2023-12-08 11:33:29 +01:00
nicolas.dorier
d78267d7ee Bump NTag lib 2023-12-08 16:22:49 +09:00
d11n
0c16492d1c Payment details: Re-add unit for displayed amount (#5541)
* Payment details: Re-add unit for displayed amount

Fixes #5540.

* Ensure we are not using the symbol for BTC
2023-12-07 20:40:13 +09:00
nicolas.dorier
eda437995f Show Warning is browser safari/brave is incompatible with vault on all pages 2023-12-07 14:00:30 +09:00
Dennis Reimann
379286c366 Webhooks: Remove OverPaid property from invoice payment events
In addition to #5538 and #5496. This aligns it with [what we have in the docs](https://docs.btcpayserver.org/API/Greenfield/v1/#tag/Webhooks).
2023-12-06 14:48:34 +01:00
d11n
3f344f2c0c Webhooks: Re-add OverPaid property to WebhookInvoiceSettledEvent (#5538)
Fixes #5496.
2023-12-06 09:21:04 +09:00
Nicolas Dorier
d050c8e3b2 Boltcard integration (#5419)
* Boltcard integration

* Add API for boltcard registration
2023-12-06 09:17:58 +09:00
nicolas.dorier
b13f140b86 bump 2023-12-05 20:23:58 +09:00
d11n
7066a2a577 Keypad: Show recent transactions only when logged in (#5534)
Fixes #5530. For the use case of giving access to cashiers we need to find another solution than showing the recent transactions for signed out users.
2023-12-04 22:14:37 +09:00
nicolas.dorier
a8ebaa6784 bump lightning libs and dapper 2023-12-04 18:52:40 +09:00
shuoer86
60ff7e86b8 Fix typos (#5529) 2023-12-02 10:13:28 +01:00
d11n
44b7ed0e6e Store Branding: Refactoring and logo as favicon (#5519)
* Store Branding: Refactoring and logo as favicon

- Encapsulates store branding properties into their own view model
- Uses the logo as favicon on public pages

* Refactorings

* Updates
2023-12-01 16:13:44 +01:00
d11n
afed3a0899 Dev env: Fix Lightning config warning (#5513) 2023-12-01 10:55:05 +01:00
Andrew Camilleri
28265b30d2 Support BIP129 Multisig wallet import (#5389) 2023-12-01 10:54:13 +01:00
Andrew Camilleri
a97172cea6 Pluginize Webhooks and support Payouts (#5421)
Co-authored-by: d11n <mail@dennisreimann.de>
2023-12-01 10:50:05 +01:00
Andrew Camilleri
605741182d enhance fine grain permissions (#5502)
Co-authored-by: d11n <mail@dennisreimann.de>
2023-12-01 09:12:02 +01:00
Andrew Camilleri
2c94a87be4 Support adjusting form invoice amount by multiplier (#5463)
Co-authored-by: d11n <mail@dennisreimann.de>
2023-12-01 09:10:58 +01:00
Dennis Reimann
6f98d5aa20 Fix build and warnings 2023-11-30 16:48:24 +01:00
Wojciech Nagórski
3d08e70101 Update SSH.NET to 2023.0.0 (#5404)
Co-authored-by: Nicolas Dorier <nicolas.dorier@gmail.com>
2023-11-30 18:25:09 +09:00
d11n
bdf56c0a6f Keypad: List recent transactions (#5478)
* Keypad: List recent transactions

Closes #5379.

* UI updates

* Optional: No border

* Fix class

* Decrease keypad max-width
2023-11-30 18:19:03 +09:00
d11n
b9b3860e6b NFC improvements (#5509)
* Move NFC code on Vue app level

* Update NFC result handling and display

* Save a bit of space

* Scroll NFC error into view
2023-11-30 18:17:23 +09:00
Andrew Camilleri
b0554bbf17 Send notification when a new plugin version is available (#5450) 2023-11-30 18:12:44 +09:00
d11n
b31f1812d2 Greenfield: Remove unused checkout type setting from POS (#5512)
Cam across this while browsing the API docs: The checkout type setting isn't used for the POS, so we should simply remove it as this is configured on the store-level.
2023-11-30 18:05:35 +09:00
Nicolas Dorier
04292d09e1 Pluginify BTCPayNetworkProvider (#5331) 2023-11-29 18:51:40 +09:00
Nicolas Dorier
1081eab9db Fix warnings (#5517)
Co-authored-by: Dennis Reimann <mail@dennisreimann.de>
2023-11-28 15:20:03 +01:00
d11n
4023b24209 Plugins: Improve crash detection on startup and hint at disabled plugins (#5514) 2023-11-28 15:19:47 +01:00
Andrew Camilleri
bac9ab08d1 Make wallet object system much more performant (#5441)
Co-authored-by: nicolas.dorier <nicolas.dorier@gmail.com>
2023-11-28 11:38:09 +01:00
Andrew Camilleri
75bf8a5086 Use Mempoolspace fees (#5490)
* Use Mempoolspace fees

Since bitcoind's fee estiomates are horrible, I would use an altenrative, but that adds a third party to the mix. We can either:
* Accept the risk (it is only for fee estimation anyway)
* Offer a toggle in the server settings
* Move this code to a plugin

* refactor

* Refactor

---------

Co-authored-by: nicolas.dorier <nicolas.dorier@gmail.com>
2023-11-28 18:26:35 +09:00
nicolas.dorier
3ffae30b95 bump bitcoin core in tests 2023-11-28 10:34:00 +09:00
Andreas Greimel
2fda9cf539 Fix qemu package in ARM Docker files (#5504)
With the new debian bookworm, the `qemu` package has been split into one package per architecture.
2023-11-28 09:33:19 +09:00
d11n
c8b9a425b8 Receipt fixes and improvements (#5505)
* Fix additional div

* Don't show payment number if there is only one

* Bump max-width to prevent wrapping in top container

* Fix colspan

* Re-add POS data

Closes #5498.

* Right-align amounts

* Re-order

* Don't show redundant receive date if there is only one payment

* Table improvements

* Unify crypto amount display

* More formatting improvements

* Only show Subtotal if there are calculations applicable to it

* Making margin on the bottom smaller to reduce expansion on Bitcoinize machines

---------

Co-authored-by: rockstardev <5191402+rockstardev@users.noreply.github.com>
2023-11-23 12:05:08 -06:00
nicolas.dorier
62865d7d88 Fix tabindex order in login view 2023-11-22 18:33:48 +09:00
Dennis Reimann
3afd24fcd7 Print button 2023-11-21 06:49:17 -08:00
Dennis Reimann
fd582aad75 Cleanup receipt print template 2023-11-21 06:49:17 -08:00
rockstardev
f9155772f5 Optimizing receipt printing, now works on POS terminal 2023-11-21 06:49:17 -08:00
d11n
2e4313bf18 Greenfield: Make checkout type V2 default for new stores (#5495) 2023-11-21 13:38:01 +01:00
d11n
5ad320ee4b Domain mapping: Redirect root app to canonical URL (#5471)
* Domain mapping: Redirect root app to canonical URL

We already redirect public app URLs to the canonical URL if there's a domain mapping — this adds the same behaviour for apps that are defined as root app as well.

* Refactor

* Refactor once more

Minor cleanups

---------

Co-authored-by: nicolas.dorier <nicolas.dorier@gmail.com>
2023-11-21 20:00:31 +09:00
d11n
d46543ae16 Public LN Node view: Consistency update (#5466) 2023-11-21 11:53:24 +01:00
Andrew Camilleri
2f23bad3bc Support the new LN lib (#5422)
* Support the new LN lib

* fix test

* do not cache factories

* try without useless userinfo in lnd

* Remove monero wallet files

* support simpler DI too

---------

Co-authored-by: Dennis Reimann <mail@dennisreimann.de>
2023-11-21 18:55:02 +09:00
d11n
6d288271cd Unify public page styles (#5462)
Based on #5413 and needs it to get merged first.

- Uses `--wrap-max-width` on `.public-page-wrap` rather than inner `.container` classes
- Applies `.tile` class to boxes and makes them connect to the edge of the screen below `400px` width.
2023-11-21 10:13:26 +01:00
d11n
b4daa76aeb Theme Switch: Refactor and add system option (#5473)
* Theme Switch: Refactor and add system option

Before, we had no way to reset the theme option to the system default. This introduces that option and refactors the theme switch to work in a simpler manner.

* Prevent account menu close on click inside

Context: #5476
2023-11-21 09:56:10 +01:00
nicolas.dorier
5bd8067328 bump some deps 2023-11-21 14:19:11 +09:00
Nicolas Dorier
9ccc42f556 Bump .NET 8.0 (#5479) 2023-11-21 14:11:17 +09:00
Nicolas Dorier
3ee4f43eb5 Remove CurrentRefund property in InvoiceData (#5494) 2023-11-21 12:52:40 +09:00
rockstardev
d1bf47a5c0 Bumping LND to 0.17.2-beta 2023-11-20 13:28:20 -08:00
d11n
ccf9cfa332 Minor cleanups (#5460) 2023-11-20 11:18:19 +01:00
d11n
773f8a9aea Apps: Filter list lookups by available app types (#5482)
* Apps: Filter list lookups by available app types

Uniunstalling a plugin might lead to then unavailable app types, as the entries remain in the database. The list lookups need to account for that, otherwise unavailable apps cause crashes and misbehaviour.

Fixes #5480.

* Make a hashset

---------

Co-authored-by: nicolas.dorier <nicolas.dorier@gmail.com>
2023-11-20 10:48:56 +09:00
d11n
dd62e166a1 BIP329: Use application/jsonl as MIME type (#5489)
There's an [ongoing discussion](https://github.com/wardi/jsonlines/issues/19) about what the MIME type for [JSONL](https://jsonlines.org/) files should be. Making it `application/jsonl` leads to the file being downloaded according to my testing, which prevents browsers from opening them in a new window and parsing them as JSON, which fixes #5488.
2023-11-20 10:46:36 +09:00
d11n
2fb72d5aa6 Payment Request: Improve public view (#5413)
* Payment Request: Improve public view

Closes #4450.

* Test fix

* Extract Vue utils

* Improve payment history

* Fix amount display

* Unify receipt and payment request tables

* Re-add text confirmation for copying to clipboard

* Minor print optimizations

* Wording: Rename Description to Memo

* Open view links in new window

* View updates
2023-11-20 10:45:43 +09:00
rockstardev
46f0818765 Bumping LND to 0.17.1-beta 2023-11-14 19:45:41 -08:00
d11n
96569ae4aa POS Cart: Add options for search and categories display (#5438) 2023-11-13 13:59:14 +01:00
Kukks
f2b1e5f93e fix report crash when some values are null 2023-11-10 12:30:12 +01:00
d11n
2326894a2b Responsive editor improvements (#5449) 2023-11-09 10:27:33 +01:00
d11n
c15f02ddbf Reporting: UI improvements (#5432) 2023-11-09 10:26:00 +01:00
d11n
7708084331 Pull payment improvements (#5453) 2023-11-09 10:17:52 +01:00
d11n
696a414e95 POS Keypad: Add plus and change clear functionality (#5396)
Closes #5299.
2023-11-02 20:03:34 +01:00
d11n
c16dfb2dcb POS and Crowdfund: Improve item editor (#5418)
* POS and Crowdfund: Improve item editor

Makes it work the same way as the form editor: Drag and drop for reordering and inline editing without modal.

* Upload component
2023-11-02 19:58:03 +01:00
d11n
c979c4774c POS Cart: Horizontal scrollable filters (#5391) 2023-11-02 08:36:27 +01:00
Andrew Camilleri
e82281d273 switch pos to metadata in invoice create view (#5412)
Co-authored-by: d11n <mail@dennisreimann.de>
2023-11-02 08:13:48 +01:00
d11n
27c22d5e33 Unify list views (#5399) 2023-11-02 08:12:28 +01:00
d11n
6acc545b66 Greenfield: LNURLPay store payment method fixes (#5446) 2023-11-02 08:11:32 +01:00
Nicolas Dorier
609ec0989f Do not activate Blazor in Wizard screens (#5435) 2023-10-27 10:16:36 +02:00
Nicolas Dorier
b702621a04 Simplify vault logic by introducing a VaultClient (#5434) 2023-10-27 11:54:15 +09:00
d11n
89041a6744 Wallet Send: Fill label from BIP21 (#5428)
Fixes #5426.
2023-10-27 09:59:12 +09:00
rockstardev
c485c109e6 Bumping LND to 0.17.0-beta (#5429) 2023-10-26 10:47:03 +02:00
Nicolas Dorier
29a49d5f71 Fix: In pull payment page, the amount of claims wasn't displayed (#5427) 2023-10-25 13:51:27 +02:00
Seth For Privacy
a5fafc4864 Update Passport tooltips (#5423) 2023-10-24 13:23:10 +02:00
nicolas.dorier
a921504bcf Bump HtmlSanitizer 2023-10-18 19:33:43 +09:00
nicolas.dorier
027154a4d3 Update changelog 2023-10-18 19:27:20 +09:00
d11n
bf1a1368ff Forms: Make zip code a required field in predefined address form (#5405)
Closes #5401.
2023-10-18 19:21:56 +09:00
d11n
097ffbf8a3 Greenfield: Add missing checkout (V2) settings (#5406)
* Greenfield: Add missing checkout (V2) settings

Closes #5403.

* Fix swagger
2023-10-18 19:20:05 +09:00
nicolas.dorier
ec076d1560 Fix: BTCPayServer.HostedServices.BitpayIPNSender fail to send notifications on some locale (Fix #5361) 2023-10-18 19:07:30 +09:00
d11n
8dadfa2111 Reporting fixes (#5410) 2023-10-18 10:09:03 +02:00
ndeet
c8ee6ead0b Adjust swagger doc to latest change in Greenfield API 2023-10-18 05:31:00 +02:00
Nicolas Dorier
018e4c501d Changelog 1.11.7 (#5394)
* Changelog 1.11.7

* Apply suggestions from code review

---------

Co-authored-by: d11n <mail@dennisreimann.de>
2023-10-13 23:17:17 +09:00
Andrew Camilleri
99a0b70cfa Fix form value setter (#5387)
* Fix form value setter

* Fix test parallelization

---------

Co-authored-by: nicolas.dorier <nicolas.dorier@gmail.com>
2023-10-13 10:08:16 +09:00
d11n
314a1352ec Design system updates (#5397) 2023-10-13 09:06:22 +09:00
Dennis Reimann
901e6be21e Fix processing badge color 2023-10-12 14:52:26 +02:00
Andrew Camilleri
d58dde950e Fix pay report (#5388)
* Fix pay report

* Make sure we use 11 decimals in reports for lightning payments

---------

Co-authored-by: Nicolas Dorier <nicolas.dorier@gmail.com>
2023-10-12 13:51:50 +09:00
d11n
8ac18b74df Checkout: Prevent re-rendering of payment details rows (#5392)
Potentially fixes #5390.
2023-10-12 09:35:47 +09:00
d11n
2846c38ff5 Invoice: Unify status display and functionality (#5360)
* Invoice: Unify status display and functionality

Consolidates the invoice status display and functionality (mark setted or invalid) across the dashboard, list and details pages.

* Test fix

---------

Co-authored-by: Nicolas Dorier <nicolas.dorier@gmail.com>
2023-10-11 23:12:45 +09:00
nicolas.dorier
d44efce225 Simplify code 2023-10-11 21:49:51 +09:00
Andrew Camilleri
d3dca7e808 fix lq errors and tests (#5371)
* fix lq errors and tests

* more fixes

* more fixes

* fix

* fix xmr
2023-10-11 21:12:33 +09:00
d11n
41e3828eea Reporting: Improve rounding and display (#5363)
* Reporting: Improve rounding and display

* Fix test

* Refactor

---------

Co-authored-by: nicolas.dorier <nicolas.dorier@gmail.com>
2023-10-11 20:48:40 +09:00
A. I. Oleynikov
9e76b4d28e Fix swagger (#5380) 2023-10-10 14:15:07 +09:00
d11n
ef03497350 Fix build warning (#5355)
Removes unused `string payoutSource` and shortens return with to ternary operator.
2023-10-10 12:30:48 +09:00
d11n
e5a2aeb145 Pull Payment: Add QR scanner for destination and infer payment method (#5358)
* Pull Payment: Add QR scanner for destination and infer payment method

Closes #4754.

* Test fix
2023-10-10 12:30:09 +09:00
d11n
229a4ea56c Invoice: Improve payment details (#5362)
* Invoice: Improve payment details

Clearer description and display, especially for overpayments. Closes #5207.

* Further refinements

* Test fix
2023-10-10 12:28:00 +09:00
Andrew Camilleri
f20e6d3768 Greenfield: allow delete user by email too (#5372) 2023-10-10 12:26:23 +09:00
d11n
1d210eb6e3 Crowdfund: Improve no perks case (#5378)
If there are no perks configured, do not display the perks sidebar and contribute custom amount directly, when the main CTA "Contribute" is clicked.

Before it opened a mopdal, where one had to select the only option (custom amount) manually — so this gets rid of the extra step.

Closes #5376.
2023-10-06 22:58:02 +09:00
Andrew Camilleri
d8422a979f Fix number of rates (#5365)
* Ripio had api changed
* Exchange rate host now requires an api key so removed
* Removed unused argoneum rate provider code
* switched cop and ugx to yadio
* bumped exchange sharp lib as poloniex api changed and rate source was not working
2023-10-06 16:08:50 +09:00
Andrew Camilleri
0cf6d39f02 If shitcoins are removed, dont try to hash its cryptocode for nbx (#5373) 2023-10-06 16:06:17 +09:00
Kukks
076c20a3b7 attempt to fix different casing in cryptocode of payments 2023-09-29 13:03:18 +02:00
d11n
0cfb0ba890 Email Rules: Require either recipients or customer email option (#5357) 2023-09-28 08:36:12 +02:00
nicolas.dorier
44a7e9387e bump 2023-09-27 17:02:49 +09:00
Kukks
e71954ee34 update lnurl 2023-09-27 09:13:12 +02:00
Nicolas Dorier
9cd9e84be6 Fix: After a while, a busy server would send error HTTP 500 (#5354)
This was due to Blazor which attempt to reconnect when the connection
is broken.

Before this, it would try again indefinitely, with this PR, it tries
only for around 3 minutes.

After this, the Blazor circuit should be dead anyway, so it's useless
to try again.
2023-09-27 16:05:57 +09:00
d11n
25af9c4227 Improve receipt info display (#5350)
* Improve receipt info display

Displays the info in correct order and adds optional info if tip was given with a percentage.

* Test fix

---------

Co-authored-by: Nicolas Dorier <nicolas.dorier@gmail.com>
2023-09-26 22:50:04 +09:00
nicolas.dorier
72a99bf9a6 Recommend Yadio for ARS currency (see #5347) 2023-09-26 22:21:53 +09:00
nicolas.dorier
f1228523cb Try fix flackiness of CanUsePullPaymentsViaUI 2023-09-26 22:20:25 +09:00
nicolas.dorier
a45d368115 Use exchangeratehost as recommended rates for COP 2023-09-26 21:19:42 +09:00
Nicolas Dorier
16433dc183 Hide 'Connection established' when connection to server come back (#5352) 2023-09-26 16:40:02 +09:00
Nicolas Dorier
0a956fdc73 Remove some useless intermediary type from Rate Source (#5351) 2023-09-26 16:37:40 +09:00
nicolas.dorier
75396f491b Fix: Exchangerate.host falsly appear as Yadio in the UI (Fix #5347) 2023-09-26 14:45:46 +09:00
nicolas.dorier
66a064e78b Disable prism if old version 2023-09-22 23:43:06 +09:00
Nicolas Dorier
c4f8c4c7b4 Update changelog and bump (#5341)
* Update changelog and bump

* Update Changelog.md

Co-authored-by: d11n <mail@dennisreimann.de>

* Update Changelog.md

Co-authored-by: d11n <mail@dennisreimann.de>

* Update Changelog.md

Co-authored-by: d11n <mail@dennisreimann.de>

* Update Changelog.md

Co-authored-by: d11n <mail@dennisreimann.de>

* Update Changelog.md

Co-authored-by: d11n <mail@dennisreimann.de>

---------

Co-authored-by: d11n <mail@dennisreimann.de>
2023-09-22 21:32:57 +09:00
Kukks
22bbafa659 bump lnurl 2023-09-22 12:22:46 +02:00
Nicolas Dorier
8cdfaba20c Fix: Revert to default block explorer button wasn't working (#5340)
* Fix: Revert to default block explorer button wasn't working

* Update BTCPayServer/Views/UIServer/Policies.cshtml

Co-authored-by: Andrew Camilleri <evilkukka@gmail.com>

* Update BTCPayServer/Views/UIServer/Policies.cshtml

Co-authored-by: Andrew Camilleri <evilkukka@gmail.com>

* Improve UI

---------

Co-authored-by: Andrew Camilleri <evilkukka@gmail.com>
Co-authored-by: Dennis Reimann <mail@dennisreimann.de>
2023-09-22 18:51:54 +09:00
d11n
7da82826fb API: Document payment method IDs (#5332)
* API: Document payment method IDs

This seems to be a source of confusion (see e.g. #5330), so I thought it'd be best to document the payment method IDs as an enum, so that we can refer to it in the several places they are used.

* Remove enum
2023-09-22 18:49:20 +09:00
d11n
9a46a64cad Test fixes (#5342)
* Test fixes

* Update BTCPayServer.Tests/ThirdPartyTests.cs

* Update BTCPayServer.Tests/ThirdPartyTests.cs
2023-09-22 11:48:59 +02:00
Andrew Camilleri
33198d693d Introduce archive pull payment permission and add Show QR code in view pull payment view (#5274)
* Introduce archive pull payment permission

* Add show qr option on pull payments

* Fix test

* update docs

* fix test

* Minor UI updates

* Update wording

---------

Co-authored-by: Dennis Reimann <mail@dennisreimann.de>
2023-09-22 10:24:53 +02:00
d11n
eaeb7021d5 Fix POST redirect form submit (#5336)
The `submit()` method cannot be invoked on forms without a submit button. This changes it to call the method via the JS prototype, which can be seen as a workaround.

Checked this in Firefox and Chrome. Fixes #5335.
2023-09-22 15:03:57 +09:00
Nicolas Dorier
b443acd43b Fix: Transient error 500 when accessing the wallet page (#5326) (#5328) 2023-09-19 17:52:33 +09:00
Nicolas Dorier
616883648f Move bitcoin payment data specific stuff in NBXplorerListener (#5294) 2023-09-19 10:32:41 +09:00
d11n
7873f94848 Email Rules: Add default texts and document placeholders (#5314)
Applies default subject and body text on editing to simplify email rule setup. Once the text is edited manually, the defaus  aren't applied on switching the rule type.

Also documents the placeholders that can be used.
2023-09-19 10:10:36 +09:00
d11n
17d1832dad Payment Request: Add processing status for on-chain payments (#5309)
Closes #5297.
2023-09-19 10:10:13 +09:00
d11n
f034e2cd65 Wallet Receive: Update address formatting (#5313)
* Wallet Receive: Update address formatting

Closes #5311.

* Fix tests
2023-09-19 09:56:11 +09:00
Andrew Camilleri
19de73f9da Allow configuring nfc permission beforehand (#5319)
* Allow configuring nfc permission beforehand

* UI improvements

---------

Co-authored-by: Dennis Reimann <mail@dennisreimann.de>
2023-09-19 09:55:47 +09:00
Andrew Camilleri
00acbccd7f Add payouts report (#5320) 2023-09-19 09:55:15 +09:00
d11n
77d8e202d3 Wallet: Delete custom labels (#5324)
* Tom Select improvements

* Wallet: Delete custom labels

Closes #5237.
2023-09-19 09:55:04 +09:00
Nicolas Dorier
44df8cf0c5 Rewrite the Notification dropdown with Blazor (#5325)
* Rewrite the Notification dropdown with Blazor

* Test fix

---------

Co-authored-by: Dennis Reimann <mail@dennisreimann.de>
2023-09-18 10:55:05 +09:00
d11n
e694568674 Blazor Server: Improve wording (#5323) 2023-09-17 18:45:57 +02:00
d11n
163f805f3b Plugins: Add hook for resolving Lightning Address (#5322) 2023-09-14 12:53:48 +02:00
d11n
492512f527 Dashboard: Show revenue data for keypad (#5317)
* POS: Display fixes

* Dashboard: Fix Top Items component

* Dashboard: Fix App Sales component

* Dashboard: Show revenue data for keypad

Closes #5303.
2023-09-14 09:26:47 +09:00
Nicolas Dorier
73a4ac599c Add Blazor server (#5312)
* Add Blazor server

* Improve Blazor status UI

* Improve UX

---------

Co-authored-by: Dennis Reimann <mail@dennisreimann.de>
2023-09-13 13:13:15 +09:00
d11n
4aedf76f1f Dashboard: Paid invoices in the last 7 days (#5316)
Adjust the prior number of transactions metric as discussed with @pavlenex. We now show the number of paid invoices instead of transactions, as this metric is more meaningful.

Closes #5300.
2023-09-13 09:02:02 +09:00
Nicolas Dorier
2d38113c66 Remove legacy confusing export (#5293) 2023-09-12 16:33:37 +09:00
d11n
445e1b7bd9 NFC: Fix error display (#5305)
Simple fix, the wrong variable was used. Fixes #5298.
2023-09-12 13:48:19 +09:00
d11n
019ac7ae31 Checkout: Cheating improvements (#5315)
Minor updates to the cheating options:
- Some browsers do not submit disabled fields, hence I made the amount field readonly in case of Lightning.
- Convert remaining amount when switching from onchain BTC to Lightning sats.
2023-09-12 13:48:01 +09:00
d11n
2b3b025bd8 Login: Re-add Remember Me button (#5307)
Closes #5302.
2023-09-12 12:16:37 +09:00
d11n
57bc90ad03 Archive stores and apps (#5296)
* Add flags and migration

* Archive store

* Archive apps
2023-09-11 09:59:17 +09:00
d11n
089e16020e Update LND image version (#5306)
See btcpayserver/btcpayserver-docker#828.
2023-09-10 10:07:44 +09:00
d11n
0c4f31794d Test fix: Update rate retrieval skipping parameters (#5308) 2023-09-09 09:46:09 +02:00
Kukks
cdffe9b355 Bump LNURL 2023-09-07 10:02:32 +02:00
nicolas.dorier
5a28cf9e87 Release new version of client 2023-09-06 08:21:46 +09:00
Nicolas Dorier
3b05de7f30 Fix: Crash caused by very old point of sales invoices (#5283) (#5291) 2023-09-05 15:32:49 +09:00
nicolas.dorier
79b2f1652b Changelog and bump 2023-09-02 23:22:59 +09:00
nicolas.dorier
b32e0e7cce Fix #5233: Error on the MigrationStartupTask 2023-09-02 23:12:37 +09:00
A. I. Oleynikov
1f9fbbee22 Update README (#5282)
Co-authored-by: A. I. Oleynikov <self@oleynikov.ai>
2023-09-01 16:03:51 +02:00
Vincent Bouzon
8c9f325c9f Display wallet balance in default currency (#5281)
* Display wallet balance in default currency

* Cleanups

---------

Co-authored-by: Dennis Reimann <mail@dennisreimann.de>
2023-09-01 15:29:41 +09:00
d11n
9bf1e35bf4 Use _blank link targt for payment scheme links (#5284)
In addition to #5270. Fixes #5266.
2023-08-31 09:44:10 +09:00
nicolas.dorier
32e830a1c5 Fix slow 'Fasts' tests 2023-08-28 09:40:44 +09:00
nicolas.dorier
561bae071f Timeout CanGetRateCryptoCurrenciesByDefault 2023-08-26 21:13:55 +09:00
nicolas.dorier
08b6942c59 Bump, changelog 2023-08-26 21:03:45 +09:00
Andrew Camilleri
4564f9a46c Small improvements (#5273)
* BUmp LNURL

* Show app view link in nav when not enoguh permission to modify

* FIx permission misalignment on create pull payments

We have explicit permissions for pull payment creation, even allow them to be created through the invoices, but the create ui and cta were blocked behind  canmodify store permission.

* Make Ln address pass an invoiceId in the context to resolve breaking change
2023-08-26 20:50:07 +09:00
d11n
58a1c6d2c8 Parse POS string data for invoice details display (#5275)
* Parse POS string data for invoice details display

Fixes #5240.

* Improve POS data display
2023-08-26 20:48:48 +09:00
Kukks
97acec340c fix lnaddress nav item permission 2023-08-24 16:31:49 +02:00
nicolas.dorier
52790a6954 bump clightning 2023-08-24 20:59:15 +09:00
nicolas.dorier
af6249a741 bump lightning lib 2023-08-24 20:59:15 +09:00
d11n
17064ab3c8 POS: Unify item display in editor (#5272) 2023-08-24 08:51:22 +02:00
d11n
1487bf4ff5 Unset link targt for payment scheme links (#5270)
Potential fix for #5266 — see the discussion in that issue for details.

This change should be non-invasive, I tested the links in regular as well as modal mode and they worked in Firefox, Brave and Chrome.
2023-08-24 13:37:27 +09:00
d11n
e8c0858558 POS: Fix alignment of items in static view (#5271)
Items in static view weren't center aligned. This matches the classes in the cart view. Fixes #5230.
2023-08-23 11:11:41 +02:00
nicolas.dorier
56fa3fe8f2 Fix crash on /wallets/transactions with non zero skip parameter (Fix #5183) 2023-08-23 16:11:25 +09:00
nicolas.dorier
583813883c Simplified logic for receipt amount (#5197) 2023-08-23 10:43:34 +09:00
Andrew Camilleri
c69f95bdce Do not block payments on LN while syncing if it is not internal node (#5269) 2023-08-22 13:45:50 +02:00
Kukks
b3df403980 Fix LN payout manual payments UI crashing when payouts are not tied to pull payment 2023-08-15 15:11:04 +02:00
dstrukt
90ce75ee21 remove store ID from view request url (#5256) 2023-08-13 19:26:21 +02:00
Kukks
1c5fcfe094 bump v 2023-08-11 15:55:11 +02:00
Dennis Reimann
45c1fb42ee Changelog v1.11.2 2023-08-11 15:39:08 +02:00
d11n
64bd493996 POS: Unify item display (#5252)
Display unifications for static and cart view.
2023-08-11 15:37:43 +02:00
d11n
ec6029409e Improve invoice filter wording (#5251)
Closes #5250.
2023-08-11 15:08:44 +02:00
d11n
c0fc31c69a Improve invoices status filter (#5248) 2023-08-10 20:23:18 +02:00
d11n
b5d0188f21 Receipt improvements (#5239) 2023-08-10 13:57:54 +02:00
d11n
0ccbaf4bd6 Greenfield: Fix invoice lookup by capitalized status (#5245)
All statuses need to be lowercased before lookup, this wasn't the case for e.g. `Expired`.

Fixes #5244.
2023-08-10 13:34:09 +02:00
d11n
ed43fb2071 POS fixes (#5241) 2023-08-09 14:47:28 +02:00
d11n
d67ebd957e POS: Handle flexible price items in cart view (#5238) 2023-08-09 09:31:19 +02:00
Ikko Eltociear Ashimine
19d360a543 Fix: typo in InvoiceEntity.cs (#5236)
Minumum -> Minimum
2023-08-07 09:26:37 +02:00
d11n
7dc41ebcea Email Rules: Improve validation (#5234)
Came across this while testing things and the "Please fill all required fields before testing" message wasn't clear, because the required fields were not marked.

Co-authored-by: Andrew Camilleri <evilkukka@gmail.com>
2023-08-07 09:10:48 +02:00
d11n
1eb7c727f3 POS fixes (#5228) 2023-08-05 10:44:59 +02:00
evanc-ole
ede8171408 Checkout: Fix language select UI bug (#5229) 2023-08-04 07:44:50 +02:00
Kukks
2538f3d8f6 fix https://github.com/Kukks/BTCPayServerPlugins/issues/18 2023-08-03 20:48:46 +02:00
Pavlenex
ac64f5e395 Merge pull request #5227 from dennisreimann/supporters
Update supporters
2023-08-03 19:45:09 +02:00
Dennis Reimann
1a7a731b54 Update supporters
Improve colors and visual balance
2023-08-03 14:58:32 +02:00
ndeet
86f4d48bcb c-lightning to CLN; remove ptarmigan. (#5220) 2023-08-01 17:21:00 +03:00
Kukks
83536bee88 Fix BTG rate provider 2023-07-29 10:00:34 +02:00
Kukks
abfd6ea1dc update changelog and version 2023-07-29 09:48:47 +02:00
Kukks
688e873f7a fixes #5203 2023-07-29 09:15:12 +02:00
Kukks
c88df08350 fixes #5208 2023-07-29 09:15:11 +02:00
Kukks
82586590a7 potentially fixes #5203 2023-07-29 09:15:11 +02:00
Kukks
88c66f30f2 fixes #5204 2023-07-29 09:15:10 +02:00
Kukks
9132592717 fixes #5205 2023-07-29 09:15:10 +02:00
Kukks
c0ffab768a fix ident 2023-07-29 09:15:10 +02:00
dstrukt
69190081c8 ui+checkout: fix language cutoff bug (#5210) 2023-07-28 21:24:30 +02:00
Kukks
093206cf1e add changelog 2023-07-27 15:19:48 +02:00
Kukks
a0110b7570 Merge remote-tracking branch 'origin/feat/changelog-1.11' 2023-07-27 15:14:53 +02:00
pavlenex
6d65feca4c update changelog 2023-07-27 08:39:58 +02:00
Andrew Camilleri
95be0242b6 add opensats and update strike logo (#5202)
Co-authored-by: pavlenex <pavle@pavle.org>
2023-07-27 08:39:40 +02:00
rockstardev
79e121c3af Disabling playing of the invoice sound for existing stores 2023-07-26 10:42:00 -05:00
rockstardev
676ac2fe46 Changelog 1.11.0 2023-07-26 09:11:26 -05:00
rockstardev
8eabdab53a Preventing entering of negative tips and discounts in POS 2023-07-26 07:26:53 -05:00
rockstardev
957fb09ffc Reverting logic of how paid amount is displayed on the receipt 2023-07-26 07:26:32 -05:00
nicolas.dorier
4bffe117a9 Do not show cheatmode in release, fix warnigns 2023-07-25 10:50:34 +09:00
nicolas.dorier
05b01a13c8 Fix NRE error in PoS report 2023-07-24 23:20:17 +09:00
nicolas.dorier
08e21c1a5d Fix report view 2023-07-24 23:13:11 +09:00
nicolas.dorier
4d5245605d bump 2023-07-24 22:59:18 +09:00
d11n
453548d614 Checkout v2: Play sound when invoice is paid (#5113)
* Checkout v2: Play sound when invoice is paid

Closes #5085.

* Refactoring: Use low-level audio API to play the sound

Allows to play the sound regardless of browser permissions.

* Add audio file detection

* Use model state for file upload errors

* Add default sound and customizing option

* Fix mp3 detection

* Add sounds

* Update defaults

* Add nfcread and error sounds

* Improve label wording

* Replace sound

---------

Co-authored-by: nicolas.dorier <nicolas.dorier@gmail.com>
2023-07-24 22:57:24 +09:00
Andrew Camilleri
95a0614ae1 Support accepting 0 amount bolt 11 invoices for payouts (#4014)
* Support accepting 0 amount bolt 11 invoices for payouts

* add test

* handle validation better

* fix case when we just want pp to provide amt

* Update BTCPayServer/HostedServices/PullPaymentHostedService.cs

* Update BTCPayServer/HostedServices/PullPaymentHostedService.cs

* Update BTCPayServer/Data/Payouts/LightningLike/UILightningLikePayoutController.cs

* Update UILightningLikePayoutController.cs

* fix null

* fix payments of payouts on cln

* add comment

* bump lightning lib

---------

Co-authored-by: Nicolas Dorier <nicolas.dorier@gmail.com>
2023-07-24 20:40:26 +09:00
Andrew Camilleri
36ea17a6b7 Introduce Payout metadata for api and plugins (#5182)
* Introduce Payout metadata for api and plugins

* fix controller

* fix metadata requirement

* save an object

* pr changes
2023-07-24 18:37:18 +09:00
Nicolas Dorier
dc986959fd Add reporting feature (#5155)
* Add reporting feature

* Remove nodatime

* Add summaries

* work...

* Add chart title

* Fix error

* Allow to set hour in the field

* UI updates

* Fix fake data

* ViewDefinitions can be dynamic

* Add items sold

* Sticky table headers

* Update JS and remove jQuery usages

* JS click fix

* Handle tag all invoices for app

* fix dup row in items report

* Can cancel invoice request

* Add tests

* Fake data for items sold

* Rename Items to Products, improve navigation F5

* Use bordered table for summaries

---------

Co-authored-by: Dennis Reimann <mail@dennisreimann.de>
2023-07-24 09:24:32 +09:00
d11n
845e2881fa POS Cart redesign (#5171)
* Move POS assets

* WIP

* Refactor into common Vue mixin

* Offcanvas updates

* Unifications across POS views

* POSData view fix

* Number and test fixes

* Update cart width

* Fix test

* More view unification

* Hide cart when emptied

* Validate cart

* Header improvement

* Increase remove icon size

* Animate add to cart action

* Offcanvas for mobile, sidebar for desktop

* ui+pos: updates icon size + badge + label

* Remove cart table headers

* Use same size for Cart and Shop headlines

* Update search placeholder

* Bump horizontal  input padding

* Increase sidebar width

* Bump badge font size

* Fix manipulating the quantity of line items

* Fix cart icon

* Update cart display

* updates empty button

* Rounded search input

* Remove cart button on desktop

* Fix dark accent color

* More accent fixes

* Fix plus/minus alignment

* Update BTCPayServer/Views/Shared/PointOfSale/Public/Cart.cshtml

* Apply suggestions from code review

---------

Co-authored-by: dstrukt <gfxdsign@gmail.com>
2023-07-22 21:15:41 +09:00
d11n
2e4be9310c Design system updates (#5186) 2023-07-21 09:27:37 +02:00
Nicolas Dorier
a2faa6fd59 Minor fixes (#5185) 2023-07-21 09:05:50 +02:00
Nicolas Dorier
0a78846e8d Stop using bitpay's CreateInvoice for non bitpay API usage (#5184) 2023-07-21 09:08:32 +09:00
Andrew Camilleri
4063a5aaee Quality of life improvements to payout processors (#5135)
* Quality of life improvements to payout processors

* Allows more fleixble intervals for payout processing from 10-60 mins to 1min-24hours(requested by users)
* Cancel ln payotus that expired (bolt11)
* Allow cancelling of ln payotus that have failed to be paid after x attempts
* Allow conifguring a threshold for when to process on-chain payouts (reduces fees)

# Conflicts:
#	BTCPayServer.Tests/SeleniumTests.cs

* Simplify the code

* switch to concurrent dictionary

* Allow ProcessNewPayoutsInstantly

* refactor plugin hook service to have events available and change processor hooks to actions with better args

* add procesor extended tests

* Update BTCPayServer.Tests/GreenfieldAPITests.cs

* fix concurrency issue

* Update BTCPayServer/PayoutProcessors/BaseAutomatedPayoutProcessor.cs

---------

Co-authored-by: nicolas.dorier <nicolas.dorier@gmail.com>
2023-07-20 22:05:14 +09:00
d11n
b1c81b696f Generate unique order IDs for PoS and Crowdfund sales (#5127)
* Generate unique order IDs for PoS and Crowdfund sales

Part of #5054.

* Refactorings

* Updates

* Updates

* Refactoring

* Remove search by AdditionalSearchTerm

* Implement appid

---------

Co-authored-by: nicolas.dorier <nicolas.dorier@gmail.com>
2023-07-20 16:03:39 +09:00
d11n
0017f236a7 Improve create first store view (#5181)
* Improve create first store view

Closes #5008.

* Fix tests
2023-07-19 22:21:16 +09:00
Andrew Camilleri
19d5e64063 Form invoice amount adjusters (#5158)
* Fix constant fields being editable on UI

* fix redirect to checkout if invoice is settled (redirect to receipt instead)

* enhance: make mirror field type able to map values

* Introduce invoice amount adjustment fields for form

* Integrate invoice amount adjustment fields for form on pos

* Support mirror in editor

* Indicate when special field names are used

* polsih mirror view and name suggestions for fields

* clarify

* hide hidden field from ui

* Minor adjustmentts

* Improve mirror field editing

---------

Co-authored-by: Dennis Reimann <mail@dennisreimann.de>
2023-07-19 18:54:51 +09:00
Nicolas Dorier
22435a2bf5 Refactor logic for calculating due amount of invoices (#5174)
* Refactor logic for calculating due amount of invoices

* Remove Money type from the accounting

* Fix tests

* Fix a corner case

* fix bug

* Rename PaymentCurrency to Currency

* Fix bug

* Rename PaymentCurrency -> Currency

* Payment objects should have access to the InvoiceEntity

* Set Currency USD in tests

* Simplify some code

* Remove useless code

* Simplify code, kukks comment
2023-07-19 18:47:32 +09:00
Andrew Camilleri
a7def63137 fix pos item topups lnurl (#5172)
fixes #5170
2023-07-17 13:08:41 +02:00
Kukks
3703a170e7 try fix migration for pos yml 2023-07-13 14:59:18 +02:00
Deverick
73fbfbd7cb Add support for Monero RPC authentication (#5157)
Co-authored-by: Andrew Camilleri <evilkukka@gmail.com>
2023-07-13 12:24:08 +02:00
Dennis Reimann
acae3b8753 Refactoring 2023-07-13 12:17:41 +02:00
Kukks
a618f901fc Support NFC on modal 2023-07-13 12:17:41 +02:00
Andrew Camilleri
6d4918f0ab Update ViewPullPayment.cshtml 2023-07-13 12:17:01 +02:00
Kukks
7f2c4d2e7a add extension point for pull payment view 2023-07-13 12:17:01 +02:00
Lee Salminen
fd6d361e1a CheckoutV2: When WebSocket disconnects, we should continue polling via XHR (#5165)
* When WebSocket disconnects, we should continue polling via XHR

* Update BTCPayServer/wwwroot/checkout-v2/checkout.js

Co-authored-by: d11n <mail@dennisreimann.de>

---------

Co-authored-by: d11n <mail@dennisreimann.de>
2023-07-11 21:56:13 +02:00
nicolas.dorier
b5f0924651 Serialize PosAppCartItem.value as decimal instead of string 2023-07-11 15:49:16 +09:00
d11n
1600dd4759 POS: Backwards-compatible price parsing (#5163)
* POS: Backwards-compatible price parsing

Fixes #5159 and a regression introduced in bbff9710bf: The price in posData needs to be parsed in a backwards-compatible manner, as the old format of price as an object exists in the invoice metadata.

* Test corner cases

---------

Co-authored-by: nicolas.dorier <nicolas.dorier@gmail.com>
2023-07-11 15:32:01 +09:00
d11n
c777746b69 Custom Forms: Allow HTML in labels and help text (#5136)
* Custom Forms: Allow HTML in labels and help text

Fixes #5003.

* Vue: Sanitize labels and helper text input

* Form editor: Fix blur on input for select option values

---------

Co-authored-by: Nicolas Dorier <nicolas.dorier@gmail.com>
2023-07-11 13:02:02 +09:00
nicolas.dorier
9f5466a41f Make sure CheckJsContent run as part of CI, and ignore end of line differences 2023-07-11 09:41:28 +09:00
Dennis Reimann
4d1e4801bf Dark theme color fix 2023-07-10 11:33:39 +02:00
Andrew Camilleri
5e469ff9c0 Improve rates (#5166)
* Removes Chaincoin shitcoin which is so dead even its website is gone
* Add ExchangeRateHost and FreeCurrencyRates as new rate providers
* Add recommended rate providers for UGX and RSD
* Fix BTX rate by switching to graviex
* Fix BTC rate by switching to exmo
* Fix LCAD rate script
2023-07-10 17:31:48 +09:00
d11n
2f3eedea5b Invoice lists: Show icons for payment methods (#5137) 2023-07-08 17:33:13 +02:00
rockstardev
5c5d6dc1e2 Bumping LND to 0.16.4-beta 2023-07-08 08:22:42 -05:00
Andrew Camilleri
fbe31ce64f Support LNURL in pay button (#5107)
* Support LNURL in pay button

* UI updates

* Cleanups

---------

Co-authored-by: Dennis Reimann <mail@dennisreimann.de>
2023-07-06 10:12:31 +02:00
Chukwuleta Tobechi
0b082138c8 Payment Requests: List view improvements (#5065)
* List invoice checkbox variant

* Remove custom css

* Improve payment requests list view

* Improve Payment Requests List View

* List invoice checkbox variant

* Remove custom css

* Improve payment requests list view

* Improve Payment Requests List View

* Update payment request (name link leads to view not edit)

* Refactoring

---------

Co-authored-by: d11n <mail@dennisreimann.de>
2023-07-06 10:02:23 +02:00
d11n
966e598f10 Apps: Add direct file upload in item editor (#5140) 2023-07-06 11:01:36 +09:00
d11n
e998340387 POS: Account for custom amount in cart view (#5151)
* Add failing test

* Account for custom amount

* Test fix
2023-07-05 17:23:15 +09:00
Aaron Dewes
f6b27cc5f9 Compare domains in lowercase
Domains are case-insensitive, so this comparision should be too.

I encountered this issue with a Citadel user who accidentially named their domain an uppercase name (Pay.example.com), but browsers automatically converted it to pay.example.com
2023-07-03 08:49:16 +02:00
Nicolas Dorier
f3dbf1e139 Allow browser to access LND config (#5128) 2023-06-30 15:08:23 +09:00
d11n
627d84fc91 Update to Bootstrap v5.3 (#5132)
Based on btcpayserver/btcpayserver-design#63
2023-06-30 09:21:27 +09:00
Nicolas Dorier
8cde8c01df Add category feature to the PoS with Cart (#5078)
* Add grouping feature to the PoS with Cart

* Improve UI

* Rename groups to categories

* Make it easier to select categories of the items

* Refactor TemplateEditor, use TomSelect for categories

* Prevent Vue code insertion

* Prevent empty categories

* Add label ids

* Add test case

---------

Co-authored-by: Dennis Reimann <mail@dennisreimann.de>
2023-06-30 09:13:15 +09:00
nicolas.dorier
983b8c1f54 Fix changelog 2023-06-27 21:32:05 +09:00
Nicolas Dorier
d666d8ea1a Changelog 1.10.3 (#5125)
* Changelog 1.10.3

* Apply suggestions from code review

Co-authored-by: d11n <mail@dennisreimann.de>

* Apply suggestions from code review

* Update changelog

---------

Co-authored-by: Andrew Camilleri <evilkukka@gmail.com>
Co-authored-by: d11n <mail@dennisreimann.de>
2023-06-27 21:23:56 +09:00
Dennis Reimann
3ed81c3a78 Greenfield: Fix missing default currency in stores API
Docs mention it should be present, but it wasn't. Fixes #5123.
2023-06-27 12:52:24 +02:00
nicolas.dorier
4afec2e2b6 Fix: Using lnaddresses on Nostr should not result in lots of invoice being created 2023-06-27 12:50:24 +02:00
d11n
db83d238d5 Crowdfund: Fix JS errors in empty state (#5121)
An empty crfowdfund with the default perk had JS errors.
2023-06-27 09:42:18 +09:00
rockstardev
fdcf7b3b7a Bumping LND to 0.16.3-beta (#5124) 2023-06-27 09:06:31 +09:00
Nicolas Dorier
53aafcf86b Fix: The current preimage of a invoice's lightning payment method should be available via API (#5111) 2023-06-23 19:12:11 +09:00
d11n
aec84f6d67 Dashboard: Limit "Top Items" to five (#5110)
Feedback we got at BTCPrague: Do not show more than five items in the top list, because otherwise the list can get very long if there's a POS with many items.
2023-06-23 11:31:05 +02:00
d11n
01e9f82d24 Policies: Update wording to fit API keys and Roles (#5106)
* Policies: Update wording to fit API keys and Roles

Closes #5021.

* API keys: Improve spacing
2023-06-22 10:37:30 +02:00
Nicolas Dorier
2eff45e65c Ajaxify the wallet transaction list to avoid timeout (Fix #4987) (#5100)
* Ajaxify the wallet transaction list to avoid timeout (Fix #4987)

* Add cancellation to request to wallet transactions

* Fix tests

* Improve empty state

* Cleanups

---------

Co-authored-by: Dennis Reimann <mail@dennisreimann.de>
2023-06-22 16:09:53 +09:00
d11n
13203c3e2b Receipt improvements (#5077)
* Remove Order ID link

* Add separate print version for receipt

* Fix POS number handling and add keypad test

Fixes #5056.

* Add formatting function

* Remove OrderUrl for POS, bring back order link for receipt

* Update BTCPayServer/Plugins/PointOfSale/Controllers/UIPointOfSaleController.cs
2023-06-22 15:57:29 +09:00
d11n
82c5e0e43d Dashboard: Make invoice badges consistent with those on invoices list (#5108)
Closes #4969.
2023-06-22 15:47:12 +09:00
d11n
a1575f404b Invoices: Fix search box shrinking too small (#5105)
Fixes #5099.
2023-06-21 09:52:42 +02:00
Dennis Reimann
e1509506dc Upgrade Bootstrap-Vue and fix tooltip positioning
Fixes #4956.
2023-06-21 08:31:13 +02:00
nicolas.dorier
0c1d0d7b05 Fix: formResponse and formId missing from API's GetPaymentRequest route 2023-06-21 12:47:21 +09:00
Nicolas Dorier
ad70856af0 Fix: LN payments failed to be detected on litd (#5104) 2023-06-21 12:15:46 +09:00
nicolas.dorier
8615f120ce Fix tests 2023-06-20 22:37:05 +09:00
d11n
0d0477d661 Lightning: Relax GetInfo constraint for LNDhub connections (#5083)
* Lightning: Relax GetInfo constraint for LNDhub connections

The LNDhub-compatible implementation by LNbits does not support the `GetInfo` call for all their funding sources — see lnbits/lnbits#1182. By catching that exception in combination with the `LndHubLightningClient`, we give people the ability to still use their LNbits-based LNDhub as a Lightning node.

Fixes #4482.

* Update approach to handling unsupported GetInfo calls
2023-06-20 17:28:16 +09:00
Andrew Camilleri
b31dc30878 Make file management UI more useful (#5081)
* Make file management UI more useful

* Simplify markup

* Move file info to top

---------

Co-authored-by: Dennis Reimann <mail@dennisreimann.de>
2023-06-20 08:58:28 +02:00
nicolas.dorier
6e392f4cfb After changing PoS items in UpdatePoS ident the JSON template 2023-06-19 14:44:12 +09:00
nicolas.dorier
cc3bdc331e Fix build 2023-06-16 23:19:47 +09:00
nicolas.dorier
76faf77a1c Fix keypad view broken by previous commit 2023-06-16 23:18:47 +09:00
Andrew Camilleri
d8c0e5bf3a Add extension point to template editor (#5080) 2023-06-16 23:05:49 +09:00
d11n
28c4c320cc Checkout v2: Add return link in processing state (#5075)
* Checkout v2: Add return link in processing state

* Update copy text position
2023-06-16 23:05:08 +09:00
Nicolas Dorier
e81403ec3f Fix: Applying a discount in PoS with cart wasn't working (#5079) 2023-06-16 23:02:14 +09:00
d11n
f11424f73a Pull Payment: Support LNURL Withdraw with SATS denomination (#5041)
* Pull Payment: Support LNURL Withdraw with SATS denomination

* Refactor and add tests
2023-06-16 10:56:17 +09:00
ndeet
fa8b977016 Remove id from create webhook endpoint; fix consistency. (#5045) 2023-06-16 10:53:41 +09:00
d11n
d181846339 Refund: Fix overpaid option (#5076)
Closes #5066.
2023-06-16 10:52:52 +09:00
Nicolas Dorier
1956919886 Do not crash when an invoice have an amount that is too big (#5070) 2023-06-16 10:47:58 +09:00
Dennis Reimann
0f66498965 NFC: Do not start scanning if unsupported
Fixes #5067.
2023-06-14 09:14:09 +02:00
Nicolas Dorier
918cd152b1 Fix: Incorrect rounding in the receipt of PoS invoice (fix #5071) (#5072) 2023-06-13 20:34:21 +02:00
d11n
d3222df396 Fix build warnings (#5069)
Fixes these two:

```
/source/BTCPayServer/Hosting/MigrationStartupTask.cs(643,49): warning CS0168: The variable 'items' is declared but never used [/source/BTCPayServer/BTCPayServer.csproj]
/source/BTCPayServer/Hosting/MigrationStartupTask.cs(644,24): warning CS0168: The variable 'newTemplate' is declared but never used [/source/BTCPayServer/BTCPayServer.csproj]
```
2023-06-13 20:46:44 +09:00
d11n
a84ffd8c7e Crowdfund: Fix null pointer exception for topup type (missing price) (#5068)
Items with type topup have a price = null and hence not even the property set (ignored in JSON). This needs to be handled in the temlate, otherwise this exception occurs:

```
An unhandled exception was thrown by the application.
System.InvalidOperationException: Nullable object must have a value.
   at AspNetCoreGeneratedDocument.Views_Shared_Crowdfund_Public_ContributeForm.<>c__DisplayClass24_0.<<ExecuteAsync>b__0>d.MoveNext()
```
2023-06-13 20:46:27 +09:00
Kukks
6d0f9120b8 prep for 1.10.2 2023-06-07 18:02:51 +02:00
Kukks
aafb4a7f2a Fix stale invoice api for settle invoice
fixes #5049
2023-06-07 17:57:03 +02:00
nicolas.dorier
ae432ff237 Fix: Crash on migation of old instances (Fix #5051) 2023-06-07 10:20:39 +02:00
Dennis Reimann
cdc318c71a Pay Button: Fix circular reference when serializing JSON
When apps were set, the `GetAllApps` included the store data, which led to a circular reference when serializing the JSON. That data isn't necessary here, so we can just drop it before rendering.

Fixes #5038.
2023-06-05 12:35:06 +02:00
Dennis Reimann
94d1cec8a9 Hide Sensitive Info: Fix script location
The script snippet needs to be located outside of the theme if-conditions, otherwise it only works if no custom theme is applied.
2023-06-05 12:34:54 +02:00
nicolas.dorier
c0bc19ea59 Update Changelog 2023-06-02 18:21:57 +09:00
nicolas.dorier
6f07714cd9 Language update 2023-06-02 18:18:10 +09:00
nicolas.dorier
a9d2cac23c bump 1.10.1 2023-06-02 18:15:56 +09:00
Nicolas Dorier
693b46126b Bump Bitcoin core to 25.0 (#5032) 2023-06-02 16:41:35 +09:00
Kukks
bbff9710bf fix cart + form combination bug fixes #5031 2023-06-02 09:34:55 +02:00
nicolas.dorier
358e122775 Fix tests 2023-06-01 22:17:42 +09:00
Andrew Camilleri
3818468932 Pluginify on chain wallet setup (#4999)
* Pluginify on chain wallet setup

This PR fixes a few logical points in the wallet setup flow to allow more extensive plugin flexibility; It also fixes an issue when building plugins that requires an Altcoin config profile. Here is an example showcasing the Liquid+ plugin using this to enforce that it is a hot wallet (a requirement it has) and that import to RPC is always set, and a new option that is used to configure the wallet further https://i.imgur.com/pDPQ73v.gif

* Update BTCPayServer/Controllers/UIStoresController.Onchain.cs

* update nbx
2023-06-01 21:18:28 +09:00
Kukks
3d2554fbe1 Make role name show uneditable when not creating 2023-05-31 15:49:34 +02:00
Kukks
4309603317 Hide topup items from cart 2023-05-31 15:49:34 +02:00
Dennis Reimann
f733c9ea77 Form Builder: Improve wording
Element -> Field. Something bas and I came across while reviewing the blog post.
2023-05-31 14:57:11 +02:00
Kukks
775ee01171 fix store role deletion fixes #5027 2023-05-31 13:42:38 +02:00
nicolas.dorier
33ec790137 Changelog 2023-05-31 11:50:10 +09:00
d11n
0c575c888c Remove payment requirement for marking expired invoices (#5006)
* Remove payment requirement for marking expired invoices

Allows to manually mark expired invoices, regardless of registered payments. See dennisreimann/btcpayserver-plugin-lnbank#34 for context, in which BTCPay Server sometimes did not register payments that were received to a LNbank wallet (this got fixed in btcpayserver/BTCPayServer.Lightning#129)

* Refactor conditions for better readability
2023-05-31 11:49:01 +09:00
nicolas.dorier
24f7e51e3a Small adjustements 2023-05-31 11:27:03 +09:00
Nicolas Dorier
0a0cf97c55 Do not cleanup unreachable stores (#5025) 2023-05-31 11:22:37 +09:00
Dennis Reimann
16b988d097 UI: Only display applicable refund options
Fixes #5019.
2023-05-30 12:51:51 +02:00
Dennis Reimann
5edc0ff6ef UI: Fix visual bug with Hide Sensitive Info
Fixes #5011
2023-05-30 11:20:58 +02:00
Dennis Reimann
375b96e508 UI: Center-align recovery phrase
Fixes #5007.
2023-05-30 11:19:16 +02:00
Dennis Reimann
1e72b12074 UI: Store selector link distinguishes between owner and user
The `IsOwner` property went missing with #4940, so everyone landed on the invoices list when switching stores. This brings back the original behaviour of linking to the Dashboard, if the user has the permission to access it.

Fixes #5015.
2023-05-30 11:18:34 +02:00
Kukks
4a6d52f78e Fix crowdfund perk support fixes #5013 2023-05-30 10:34:48 +02:00
Kukks
35dd580e74 Fix cart view and provide better naming for default items fixes #5012 2023-05-30 10:05:31 +02:00
Kukks
79836ef1de make free invoices from pos redirect to receipt and make receipt reload fast on such case 2023-05-30 10:04:23 +02:00
Kukks
8cb06f9c6c fix user store reole setting fixes #5010 2023-05-30 09:37:14 +02:00
nicolas.dorier
215a36e7a9 Fix: Some multi path payment on LND wouldn't be detected 2023-05-30 12:26:30 +09:00
Nicolas Dorier
247f6b86a5 Changelog 1.10.0 (#5004) 2023-05-29 16:24:27 +09:00
d11n
a9d42f1e6a Add What's New in v1.10.0 (#4992)
* Add What's New in v1.10.0

* Update BTCPayServer/Views/UIStores/Dashboard.cshtml

Co-authored-by: B <102448109+Bas02@users.noreply.github.com>

---------

Co-authored-by: B <102448109+Bas02@users.noreply.github.com>
2023-05-29 16:08:46 +09:00
nicolas.dorier
4e03c2523a Prune webhook data from database 2023-05-29 09:02:47 +02:00
nicolas.dorier
418b476725 Bug fix on StoreRoleId parsing 2023-05-27 12:51:48 +09:00
Andrew Camilleri
783e4ccb35 Store Custom Roles (#4940) 2023-05-26 23:49:32 +09:00
nicolas.dorier
6b7fb55658 Fix: Payment not marked as settled even if the payment is successful with LNBank
Fix https://github.com/dennisreimann/btcpayserver-plugin-lnbank/issues/33
2023-05-25 21:09:13 +09:00
Nicolas Dorier
3d5361cd11 [Bug] If a altcoins is disabled from BTCPay and payout processor is used, it would crash at restart (#4997)
Co-authored-by: Andrew Camilleri <evilkukka@gmail.com>
2023-05-25 12:42:23 +02:00
nicolas.dorier
2c4349c630 Test concurrent payment of lightning invoices 2023-05-25 18:41:17 +09:00
d11n
3589417b58 Form Editor: Minor wording adjustments (#4998) 2023-05-25 08:51:03 +02:00
d11n
55203e0b64 Dashboard: Fix SATS denomination display (#4994)
When the default currency of the store is SATS, the display was broken.
2023-05-25 10:08:00 +09:00
nicolas.dorier
a918288e3b Fix codeql config to not scan vendor js, add it to solution 2023-05-23 10:38:59 +09:00
nicolas.dorier
e183138d2c Remove vendor js from codeql scan 2023-05-23 10:07:08 +09:00
Zaxounette
d3e42862ed Create codeql.yml (#4821)
Co-authored-by: Nicolas Dorier <nicolas.dorier@gmail.com>
2023-05-23 09:23:24 +09:00
Andrew Camilleri
8860eec254 Switch Apps to json not YML (#4792) 2023-05-23 09:18:57 +09:00
Kukks
97e7e60cea Add minrelayfee to payjoin request
fixes #4689
2023-05-22 14:56:08 +02:00
d11n
44aaf7acbb Form editor (#4968)
Co-authored-by: dstrukt <gfxdsign@gmail.com>
2023-05-22 13:30:28 +02:00
Nicolas Dorier
9b721fae27 Better handle postgres requests for wallet objects (#4985) 2023-05-20 23:26:16 +09:00
Nicolas Dorier
c3f412e3bb Bump tests to Bitcoin Core 24.1 (#4988) 2023-05-20 21:38:39 +09:00
nicolas.dorier
ee738a29f0 Stop spamming logs with event aggregator logging 2023-05-19 15:24:20 +09:00
d11n
6c6544bf9b Improve invoice filtering UI (#4914)
* Improve invoice filtering UI

Closes #3664.

* UI updates

* Add app filter

* Add indicator for active filters

* updates text

* Improve selected filter display

* Apply suggestions from code review

---------

Co-authored-by: dstrukt <gfxdsign@gmail.com>
2023-05-19 10:42:09 +09:00
Nicolas Dorier
3d57b944ca Fix a bunch of minor bugs (#4983) 2023-05-19 08:41:21 +09:00
Nicolas Dorier
acf003b1b4 Do not generate new address when a new payment is detected (#4984)
* Do not generate new address when a new payment is detected

* Update BTCPayServer.Tests/UnitTest1.cs

Co-authored-by: Andrew Camilleri <evilkukka@gmail.com>

---------

Co-authored-by: Andrew Camilleri <evilkukka@gmail.com>
2023-05-18 16:53:01 +09:00
d11n
52e108d32f Minor design system updates (#4973)
- Update Manage Plugins icon
- Add ESC to supporters sprite
- Update body-text-active variable
2023-05-17 10:19:26 +02:00
Nicolas Dorier
7b96f96025 bump clightning (#4970)
* bump clightning

* Remove Lightning Charge from our tests
2023-05-16 09:17:21 +09:00
Kukks
8db5e7e043 Plugins: Allow payout processors to signal they cannot be removed through common UI 2023-05-15 09:49:13 +02:00
d11n
25fb5c1293 Checkout v2: Improve expired paid partial state (#4827)
Co-authored-by: Andrew Camilleri <evilkukka@gmail.com>
2023-05-11 10:38:40 +02:00
dstrukt
37f0498def adds payouts settings button (#3857)
Co-authored-by: Dennis Reimann <mail@dennisreimann.de>
2023-05-11 10:37:28 +02:00
d11n
02110f93d7 Hide sensitive info (#4966) 2023-05-11 10:35:51 +02:00
d11n
195dfc2c47 Refund updates (#4934) 2023-05-11 10:33:33 +02:00
d11n
541b6cf9eb Improve create first store case (#4951) 2023-05-10 11:18:29 +02:00
d11n
2c26b77afc Forms: Add multiline input (#4942) 2023-05-10 11:14:19 +02:00
nicolas.dorier
99bcec5597 bump nbx 2023-05-09 22:06:23 +09:00
Andrew Camilleri
781190a65d Bump to 1.9.3 (#4955)
* Bump to 1.9.3

* Apply suggestions from code review

* Update Changelog.md

* Update Changelog.md

* Update Changelog.md

* Update Changelog.md

Co-authored-by: d11n <mail@dennisreimann.de>

* Update Changelog.md

Co-authored-by: d11n <mail@dennisreimann.de>

* Update Changelog.md

Co-authored-by: d11n <mail@dennisreimann.de>

---------

Co-authored-by: d11n <mail@dennisreimann.de>
2023-05-08 21:30:43 +09:00
d11n
3763480280 NFC: Handle HTTP-related exceptions (#4965) 2023-05-08 12:11:42 +02:00
Andrew Camilleri
6fad5ebedb Do not crash checkout when attempting lnurl checkout through non secure page (#4964)
Co-authored-by: d11n <mail@dennisreimann.de>
2023-05-08 12:09:48 +02:00
Andrew Camilleri
0690194aa1 Fix posdata with primitive array (#4954)
Co-authored-by: d11n <mail@dennisreimann.de>
2023-05-08 11:02:13 +02:00
nicolas.dorier
03b94e2be3 Minor refactoring about DefaultPaymentMethod 2023-05-08 09:14:58 +09:00
d11n
18e34b3cbe Checkout v2: Improve truncation of displayed addresses (#4924) 2023-05-05 10:00:55 +02:00
d11n
a0bb3ace61 LN Settings: Show only node host name (#4927) 2023-05-05 09:59:33 +02:00
Dennis Reimann
920ad67633 Rates: Fix advanced rules example formatting
Fixes #4920.
2023-05-05 09:58:42 +02:00
Dennis Reimann
8b8f72129c Crowdfund: Fix redirect URL fallback
As the request for invoice creation is issued via web socket, the display URL ends up being the hob connection URL. This replaces it with the actual app URL and fixes #4930.
2023-05-05 09:57:44 +02:00
Dennis Reimann
b9b11e722c Greenfield: Apply store default payment method on invoice creation
Fixes #4947.
2023-05-05 09:56:23 +02:00
Kukks
eddd458744 Fix missing shopify link
fixes #4945
2023-05-05 09:51:45 +02:00
rockstardev
439ea20a89 Resolving weird Firefox form autocomplete behavior on our POS (#4950) 2023-05-04 18:49:35 +02:00
d11n
cec223c8e7 Fix missing closing div (#4944) 2023-05-03 10:55:20 +02:00
rockstardev
25cb188d00 Bumping LND to 0.16.2-beta 2023-04-29 09:10:07 -05:00
Pavlenex
31007a8d96 Merge pull request #4929 from dennisreimann/esc
Supporters: Add ESC
2023-04-27 18:30:09 +02:00
Dennis Reimann
a4fa8db69b Supporters: Add ESC 2023-04-27 18:28:29 +02:00
nicolas.dorier
6193835ea1 Make LightningListener nullable, fix some NRE 2023-04-27 13:37:32 +09:00
rockstardev
0c78e9e4ac Bumping LND to 0.16.1-beta (#4921) 2023-04-27 13:17:57 +09:00
nicolas.dorier
58c409e7fa Fix tests 2023-04-27 13:00:25 +09:00
Nicolas Dorier
9577eed524 Validate input in greenfield for payout processors (#4922) 2023-04-27 10:59:19 +09:00
Kukks
76f32cd064 Fix payment request overriding user provided email 2023-04-26 14:06:42 +02:00
Kukks
92d9c17095 Fix payment request merge conflict marker in UI 2023-04-26 14:06:23 +02:00
nicolas.dorier
b0396df33f Update changelog 2023-04-26 18:27:06 +09:00
nicolas.dorier
c17572c76f Clip configuration values for payout processors 2023-04-26 18:24:46 +09:00
nicolas.dorier
5c91e072a6 Prevent payout processors from stalling restart 2023-04-26 18:09:56 +09:00
nicolas.dorier
4991d0f965 Update Changelog and bump version 2023-04-26 17:57:25 +09:00
Andrew Camilleri
45b74e1ce5 Fix cart receipt + fix pos email form forwarding (#4917)
* Show correct array regardless of size

fixes #4890

* Email provided to pos form was not forwarded to form

fixes #4810

* Make invoice receipt url redirect to the invoice redirect url if receipt is not enabled

When setting up a default email rule upon invoice settlement, you would link to the receipt page naturally. However, if using the payment requests, receipts are disabled as the payment request itself is the receipt.  This commit makes the receipt url redirect to the invoice redirect url if available, and in the case of payment requests, it would mean the receipt url is the payment request url. fixes #4895

* Set the email address in the form when configured in the payment request

* fix pay request email copy

* fix payouts nav link

fixes #4788

* Update BTCPayServer/Views/UIPaymentRequest/EditPaymentRequest.cshtml

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

---------

Co-authored-by: Nicolas Dorier <nicolas.dorier@gmail.com>
2023-04-26 16:45:35 +09:00
Nicolas Dorier
ccb4b9a9ba Merge pull request #4916 from dennisreimann/receipt-print
Receipt: Optimize print view
2023-04-26 15:41:41 +09:00
Nicolas Dorier
dd635071d6 Merge pull request #4909 from dennisreimann/fix-4889
POS: Fix choiceKey case
2023-04-26 15:16:54 +09:00
Nicolas Dorier
fe1448dfae Merge pull request #4911 from NicolasDorier/qiponbtq
Remove LNURLStandardInvoiceEnabled
2023-04-26 15:15:20 +09:00
Ikko Eltociear Ashimine
56855bc54d Fix typo in PayJoinEndpointController.cs (#4918) 2023-04-25 22:29:06 +02:00
nicolas.dorier
b51fa8df5a Fix indent 2023-04-25 21:03:12 +09:00
Dennis Reimann
3aa979cb11 Test updates 2023-04-25 13:07:01 +02:00
Dennis Reimann
c95f75bc6c Remove disable BOLT11 option 2023-04-25 12:21:27 +02:00
Dennis Reimann
b13a636f89 Fix syntax error 2023-04-25 12:11:08 +02:00
Dennis Reimann
8de55cef31 Unify checkout v1 behaviour with v2 2023-04-25 11:36:45 +02:00
Dennis Reimann
cb781f42e3 POS: Fix choiceKey case
I came across this while debugging #4889. This does not actually fix it, but it fixes an inconsistence in the casing of the parameter name.

However, I think the original issue is a caching problem in the browser. I was able to reproduce it on first load, after reloading the page once more it works as intended. The weird thing is: even though the values are correct on first load (verified via debugger), the `choiceKey` for the first item is set incorrectly to an integer value.
2023-04-25 09:16:09 +02:00
Dennis Reimann
b59292dc24 Receipt: Optimize print view
Fixes #4902
2023-04-25 07:30:15 +02:00
nicolas.dorier
03b793d7e2 Fix tests 2023-04-25 10:28:36 +09:00
nicolas.dorier
bee18d1cfb Can set LazyPaymentMethod at the invoice creation level 2023-04-25 08:38:42 +09:00
nicolas.dorier
d8698181f4 Remove LNURLStandardInvoiceEnabled 2023-04-24 23:52:40 +09:00
d11n
47f5d97eaf Prevent an NRE in LNURL (#4908)
* Prevent an NRE in LNURL

In addition to f05a7f9f14. Fixes #4904.

* Revert "Prevent an NRE in LNURL"

This reverts commit 0b241d61ab45b79297211e04da0e05c2cb10dada.

* Fix NRE

---------

Co-authored-by: nicolas.dorier <nicolas.dorier@gmail.com>
2023-04-24 21:50:31 +09:00
Nicolas Dorier
cb3c5e56fd Fix: Poll of Lightning Invoice status might fail on LND if LNURL is used (#4910) 2023-04-24 18:15:39 +09:00
Nicolas Dorier
39b76c08de Fix: Form in Payment Requests was not setting its values to the invoices metadata (#4907) 2023-04-24 18:04:46 +09:00
Zaxounette
c3c8cc21ff Security Page Refactor (#4815)
* Update SECURITY.md

* typo

* project vs product

* Suggestion Update - Docker Deployment

Co-authored-by: d11n <mail@dennisreimann.de>

* Suggestion - Email highlight

Co-authored-by: d11n <mail@dennisreimann.de>

---------

Co-authored-by: d11n <mail@dennisreimann.de>
2023-04-24 15:04:56 +09:00
nicolas.dorier
6dba1b6d8b Nullable on LanguageService 2023-04-20 09:44:24 +09:00
nicolas.dorier
381fe70a79 Add UserAgent to IPN and webhook HTTP requests (Fix #4883) 2023-04-19 21:13:31 +09:00
ndeet
feb927c2e4 Add missing param, fix response code. (#4891) 2023-04-19 13:07:36 +09:00
d11n
e77bd4c188 Swagger: FIx typos in pull payments docs (#4893)
Fixes #4892.
2023-04-19 13:06:51 +09:00
d11n
43436fc49e Remove BTCPayServer.Plugins.Test (#4875)
Remove the plugin, which is superseded with the plugin template and move the PluginPacker to the root of th solution (which matches its location in the file system).
2023-04-17 11:38:03 +09:00
nicolas.dorier
d738f797ec bump 2023-04-17 11:11:00 +09:00
nicolas.dorier
b5de97f785 Update changelog 2023-04-17 10:54:00 +09:00
Nicolas Dorier
b0c1b0895d Fix crash if auto detect language on checkout page, and the language couldn't be detected (Fix #4881) (#4888) 2023-04-17 10:53:45 +09:00
nicolas.dorier
8e60932f81 Migrate reference to AppType in server settings (Fix #4882) 2023-04-17 10:34:41 +09:00
d11n
7d14cd74f2 LightningLikePaymentData: Null-check preimage (#4886)
Fixes #4884.
2023-04-17 00:08:40 +02:00
nicolas.dorier
717f1610f5 Update changelog 2023-04-13 16:28:30 +09:00
ndeet
f1abe6497f Fix wrong data mapping to store data instead of store user data. (#4874) 2023-04-13 16:27:40 +09:00
rockstardev
046129a57d Bumping LND to 0.16.0-beta (#4873) 2023-04-13 14:48:47 +09:00
nicolas.dorier
90d300a490 Remove superflous category in API doc 2023-04-13 08:41:46 +09:00
d11n
a2d506c0db Checkout v2: Confetti for processing payments (#4872)
Let's not spoil the fun for those paying on-chain.
2023-04-13 08:40:21 +09:00
d11n
58748a24da What's New entry for v1.9 (#4871)
Copy is just a proposal from my side. Link check expected to fail, because the blog post isn't up yet.
2023-04-12 20:40:20 +09:00
Nicolas Dorier
639e8a4a1d Add links to invoice metadata in the docs (#4869) 2023-04-12 16:30:34 +09:00
Nicolas Dorier
48ebaf5c5a Do not put payRequest in the metadata (#4870) 2023-04-12 16:30:22 +09:00
nicolas.dorier
1aaccb1e6b Do not show empty JObject in AddtionalData of views 2023-04-11 13:21:02 +09:00
nicolas.dorier
f05a7f9f14 Prevent an NRE in LNURL 2023-04-11 13:08:14 +09:00
nicolas.dorier
98ddb348b0 Update changelog 2023-04-10 20:21:13 +09:00
Nicolas Dorier
a4aa85ebab Fix: If connection to Lightning node was interrupted, payments would be missed (#4865) 2023-04-10 19:35:01 +09:00
Nicolas Dorier
516efe56f4 Refactor: Dot not make LNURLPaymentMethodDetails depends on BTCPayInvoiceId (#4864)
* Refactor: Dot not make LNURLPaymentMethodDetails depends on BTCPayInvoiceId

* Abstract PaymentProof

* fix bug

* Make the selenium container resolves the btcpay host name
2023-04-10 16:38:49 +09:00
nicolas.dorier
a4d72d5bbc Fix tests 2023-04-10 15:06:59 +09:00
nicolas.dorier
24b8ec16f1 Fix tests 2023-04-10 13:37:09 +09:00
nicolas.dorier
ac25fef555 Remove warning 2023-04-10 12:01:11 +09:00
nicolas.dorier
8302f082a2 Do not render the TruncateCenter if text null or empty 2023-04-10 11:17:29 +09:00
nicolas.dorier
7546ef7a8e Dotnet reformat 2023-04-10 11:07:03 +09:00
nicolas.dorier
f598c70a4f Add comment TODO for improving automatic translations efficiency. 2023-04-10 11:03:36 +09:00
nicolas.dorier
422da21de5 Update Changelog 2023-04-07 17:52:48 +09:00
nicolas.dorier
f530fb3241 Update changelog 2023-04-07 17:50:57 +09:00
Andrew Camilleri
5d39bb7466 Allow LN Address to customize invoice metadata, and various bug fixes on LNUrl (#4855)
* Allow LN Address to customize invoice metadata

solves https://github.com/OpenSats/website/issues/8

* Refactor GetLNUrl

* Fix lightningAddresssettings.Max being ignored

* Fix: The payRequest generated by the callback wasn't the same as the original

---------

Co-authored-by: nicolas.dorier <nicolas.dorier@gmail.com>
2023-04-07 17:48:58 +09:00
Wouter Samaey
041cba72b6 Made Fake Custodian config field non-required (#4861) 2023-04-07 15:59:44 +09:00
Andrew Camilleri
892b3e273f Improve Labeling further (#4849)
* If loading addresses into the send wallet page using bip21 or address,  (or clicking on "Send selected payouts"  from the payotus page), existing labels will be pre-populated.
*  Add the payout label to the address when the payoutis created instead of to the transaction when it is paid.
*  Add the label attachments when adding labels from an address to the transaction.
2023-04-07 15:58:41 +09:00
nicolas.dorier
91faf5756d With core lightning, getting payment by paymenthash wouldn't return the successful payment if the first one failed. 2023-04-07 15:54:56 +09:00
Andrew Camilleri
e239390ebf Allow any bolt11 invoice for pullpayments/payouts (#4857)
closes #4830

If users want to deal with expired payout destinations, then they should be able to conifugre it that way. Some wallets simply do not allow customizing the bolt11 expiry and the defaults are much less than a day. I think we should merge #3857 if we introduce this as an automated payotu processor for lightning running every few minutes would work together with this and solve it.
2023-04-06 15:54:19 +09:00
nicolas.dorier
b24764d679 bump 2023-04-05 23:38:39 +09:00
Nicolas Dorier
4d5a568fd7 Changelog 1.9 (#4852) 2023-04-05 23:37:01 +09:00
Andrew Camilleri
5ab55e71e0 Add cart items to receipt (#4851) 2023-04-05 15:42:23 +02:00
Wouter Samaey
929d63ecf8 Fixed nav menu active states + Better Fake Custodian (#4854) 2023-04-05 15:11:46 +02:00
Kukks
0ef7f3715f remove obsolete reference 2023-04-05 15:09:42 +02:00
Nicolas Dorier
2298f3901a Update langs (#4853) 2023-04-05 13:32:24 +09:00
nicolas.dorier
3005f1937a Replace GetUriByAction by GetPathByAction 2023-04-05 10:58:56 +09:00
nicolas.dorier
f48eec2e93 Fix: Redirecting to local absolute URL in wizard windows should work 2023-04-05 10:55:35 +09:00
nicolas.dorier
754d304e54 Fix tx link in Recent Transactions 2023-04-05 08:53:34 +09:00
Nicolas Dorier
9b8d08a668 Make Checkout V2 the default (#4850)
* Make Checkout V2 the default

* Fix align
2023-04-05 08:35:50 +09:00
Nicolas Dorier
1b672a1ace Allow multi-step settings in custodian (#4838)
* Allow multi-step settings in custodian

* Fix CustodianAccount.Name not saved

* Reuse TradeQuantity for SimulateTrade

* TradeQuantityJsonConverter accepts numerics

* Fix build
2023-04-04 14:48:29 +09:00
Andrew Camilleri
60d6e98c67 Form System Flexibility improvements (#4774)
* Introduce very flexible form input system

* Refactorings after rebase

* Test fix

* Update BTCPayServer/Forms/FormDataService.cs

---------

Co-authored-by: Dennis Reimann <mail@dennisreimann.de>
2023-04-04 11:01:34 +09:00
d11n
11f05285a1 Invoice Details: Improve payments list and print view (#4817)
Closes #4729.

Co-authored-by: Nicolas Dorier <nicolas.dorier@gmail.com>
2023-04-04 10:59:14 +09:00
d11n
2bd1842da1 Improve pagination (#4828)
Based on the design repo updates by @benalleng in btcpayserver/btcpayserver-design#61. Closes #3835.
2023-04-04 10:54:04 +09:00
Nisaba
57544068e9 Store rule emails in HTML + Test rule emails (#4843)
* Store rule emails in HTML + Test rule emails

* Store rule emails in HTML + Test rule emails

* Update BTCPayServer/Controllers/UIStoresController.Email.cs

Thanks !

Co-authored-by: Andrew Camilleri <evilkukka@gmail.com>

* change in StoreEmails() to work properly with add commande

* CanSetupEmailServer() : Remove Bosy sendkeys that doesn't work with new HTML control

* Update test for new HTML control

* better command treatment

---------

Co-authored-by: nisaba <infos@nisaba.solutions>
Co-authored-by: Andrew Camilleri <evilkukka@gmail.com>
2023-04-04 10:52:42 +09:00
Nicolas Dorier
60cfea9f94 Add presets in the checkout appearance (#4756) 2023-04-04 10:45:40 +09:00
Andrew Camilleri
eece001376 Add labels for recent txs dashboard widget (#4831)
* Add labels for recent txs dashboard widget

It is not with the rich data for now, but a good start.

* Turn labels into links

* Add rich info to dashboard labels

* Use truncate-center component for recent transactions

---------

Co-authored-by: Dennis Reimann <mail@dennisreimann.de>
2023-03-30 19:54:11 +02:00
d11n
98d8ef8e1a Checkout Appearance: Move language settings into the general section (#4833)
The language settings work for both versions of the checkout, so let's make them available for both. Request by @petzsch.
2023-03-30 08:18:13 +02:00
Nicolas Dorier
0e43042217 Remove FakeCustodian as a separate project (#4839) 2023-03-30 12:38:30 +09:00
Andrew Camilleri
c8e6714207 Merge pull request #4784 from dennisreimann/unify-plugins-apps
Apps become plugins
2023-03-29 20:03:18 +02:00
Andrew Camilleri
824e779eb2 Hooks for Zaps (#4826) 2023-03-29 12:27:04 +02:00
d11n
83ea898780 Checkout: Update wording (#4829)
We make it "Payment Received" instead of "Payment Sent", because in other places we are also talking from the merchant perspective.
2023-03-29 17:24:49 +09:00
nicolas.dorier
5af3233fd6 Add link to forms doc 2023-03-28 22:02:58 +09:00
d11n
08ff2f3173 Checkout: Fix language select width in modal (#4823) 2023-03-28 13:12:47 +02:00
d11n
22657b66d7 NFC finetuning (#4822) 2023-03-28 12:36:32 +02:00
d11n
8b6c7a6061 Pull Payments: FIx column order in list (#4818)
Date and name were swapped. Fixes #4814.
2023-03-28 19:10:50 +09:00
Andrew Camilleri
1f197f6688 Merge pull request #4819 from dennisreimann/nfc 2023-03-28 09:19:58 +02:00
Dennis Reimann
1055e61bb4 NFC improvements
Two changes which fix #4807:

- Once permissions are granted we start scanning immediately, no need to ask for permissions or have the user click the button again
- We don't abort the scan, which gets rid of the cases in which the OS took over after the scan, because the user left the card on the device

Also adds feedback for the NFC states scanning and submitting.
2023-03-27 18:28:53 +02:00
Dennis Reimann
7ad0aa82fc Apps become plugins
Unified navigation for apps that are now plugins. Part of #4744.
2023-03-27 16:43:44 +02:00
Andrew Camilleri
d3f5576570 Remove store integrations list page (#4816)
Co-authored-by: d11n <mail@dennisreimann.de>
2023-03-27 16:40:50 +02:00
d11n
45141d1391 Checkout v2: Payment processing state (#4778) 2023-03-27 12:12:11 +02:00
d11n
de9ac9fd43 Receipt: Add payment proof (#4782)
* Receipt: Add payment proof

Closes #4685.

* shice

* Add truncate-center component

* Improve view

* Hide button and link when printed

* Describe component

* Remove transaction ID from UI

* Remove modification to interface

---------

Co-authored-by: nicolas.dorier <nicolas.dorier@gmail.com>
2023-03-27 14:07:12 +09:00
d11n
c53d5272d6 Wallet Transactions Export: Add BIP-329 support (#4799)
* Wallet Transactions Export: Add BIP-329 support

* Adjust wording

* Export one line per label

* Join labels, fix type

* Rewrite the ProcessBip329 function to be more performant

* Add nullable on all TransactionsExport

---------

Co-authored-by: nicolas.dorier <nicolas.dorier@gmail.com>
2023-03-27 13:59:33 +09:00
Pavlenex
18c78192ec Reconstruct issue template (#4803)
* reconstruct issue template

* provide a direct link for filing a tech question
2023-03-27 13:59:07 +09:00
nicolas.dorier
632d67eef4 Fix casing in template example for forms 2023-03-27 12:54:12 +09:00
d11n
c23aa48688 Optimize invoice print view (#4783)
Closes #4729.
2023-03-26 20:44:05 +09:00
d11n
95f3e429b4 Wallet transactions: Add label manager (#4796)
* Wallet transactions: Add label manager

* Update BTCPayServer/Views/UIWallets/WalletTransactions.cshtml

Co-authored-by: Andrew Camilleri <evilkukka@gmail.com>

* Add rich label info

* Fixes

* support labels in wallet send

* add labels to tx info page

* Remove noscript parts

* Allow click on transaction label info

* update psbt info labelstyling

* revert red pixel fix as it broke all

---------

Co-authored-by: Andrew Camilleri <evilkukka@gmail.com>
2023-03-26 20:42:38 +09:00
Chukwuleta Tobechi
8635fcfe84 UI: Redesign Recovery Seed view (#4793)
* Improve recovery seed backup page

* Fix errors from Selenium tests (Sequence contains no elements)

* Revert previous commit

* Improve recovery seed backup page

* Recovery phrase UI update

* recovery seed UI format

* Improve word order

* One column version

---------

Co-authored-by: Dennis Reimann <mail@dennisreimann.de>
2023-03-24 16:09:53 +01:00
Andrew Camilleri
d861537d9a Merge pull request #4809 from dennisreimann/gf-ln-array-fix
Greenfield: Fix Lightning transaction list return types
2023-03-23 18:59:36 +01:00
Dennis Reimann
631ee99f60 Greenfield: Fix Lightning transaction list return types
The LocalBTCPayServerClient deserializes the results as arrays (`LightningPaymentData[]` and `LightningInvoiceData[]`) — if they are `IEnumerable` the `GetFromActionResult` does not return the data but null.
2023-03-23 17:42:10 +01:00
nicolas.dorier
ffa1441ccd Delete code detecting whether the running version of nbx fixed a bug
The reason to delete this is that any version of NBX with this bug
wouldn't be able to run nowadays because of another bug which would
prevent NBXplorer from synching (Array size too big)
2023-03-23 13:45:40 +09:00
Andrew Camilleri
2f3e947027 Merge pull request #4795 from Kukks/lnurl-disable-if-no-node 2023-03-22 09:02:52 +01:00
Andrew Camilleri
a62aecfdfe Merge pull request #4798 from dennisreimann/fix-4794 2023-03-22 09:02:35 +01:00
Andrew Camilleri
5f829c68f2 Merge pull request #4797 from dennisreimann/fix-4790 2023-03-22 09:02:10 +01:00
Dennis Reimann
0290d74aeb POS: Fix escaped HTML entities in item title
Properly escapes and the sanitized values. Fixes #4794.
2023-03-21 15:31:54 +01:00
Dennis Reimann
f6bc16007d Label tooltips: Use plain text instead of HTML
Fixes #4790.
2023-03-21 15:21:24 +01:00
Dennis Reimann
ad5752f09b Reuse LightningTimeout constant 2023-03-21 14:22:10 +01:00
Kukks
55565f1718 Do not provide lnurl method if ln node is dead
fixes #3541
2023-03-21 13:48:25 +01:00
nicolas.dorier
5f96d17b8c Update lang 2023-03-20 19:30:56 +09:00
nicolas.dorier
fd22406e0a Fix PullTransifexTranslationsCore 2023-03-20 19:21:35 +09:00
nicolas.dorier
64fe542c1e Update lang 2023-03-20 19:20:46 +09:00
Andrew Camilleri
fae1dc8dbb Adapt cookie auth to work with same API permission system (#4595)
* Adapt cookie auth to work with same API permission system

* Handle unscoped store permission case

* Do not consider Unscoped as a valid policy

* Add tests

* Refactor permissions scopes

---------

Co-authored-by: Dennis Reimann <mail@dennisreimann.de>
Co-authored-by: nicolas.dorier <nicolas.dorier@gmail.com>
2023-03-20 10:46:46 +09:00
Wouter Samaey
6f2b673021 Custodian withdrawal support + Some refactoring and cleanup (#4085)
* Renamed "WithdrawAsync" to "WithdrawToStoreWalletAsync"

* WIP

* WIP withdrawal + Refactored Form saving to JObject

* WIP

* Form to fix bad values during withdrawing appears correctly

* WIP

* Lots of cleanup and refactoring + Password field and toggle password view

* Cleanup + Finishing touches on withdrawals

* Added "Destination" dummy text as this is always the destination.

* Fixed broken test

* Added support for withdrawing using qty as a percentage if it ends with "%". Needs more testing.

* Fixed broken build

* Fixed broken build (2)

* Update BTCPayServer/wwwroot/swagger/v1/swagger.template.custodians.json

Co-authored-by: d11n <mail@dennisreimann.de>

* Update BTCPayServer/wwwroot/swagger/v1/swagger.template.custodians.json

Co-authored-by: d11n <mail@dennisreimann.de>

* Improved unit tests

* Fixed swagger bug

* Test improvements

Make string conversion of quantity explicitely.

* Fix build warnings

* Swagger: Add missing operationId

* Made change Dennis requested

* Removed unused file

* Removed incorrect comment

* Extra contructor

* Renamed client methods

* Cleanup config before saving

* Fixed broken controller

* Refactor custodian

* Fix build

* Make decimal fields strings to match the rest of Greenfield

* Improve parsing of % quantities

---------

Co-authored-by: d11n <mail@dennisreimann.de>
Co-authored-by: nicolas.dorier <nicolas.dorier@gmail.com>
2023-03-20 10:45:32 +09:00
Nicolas Dorier
b26679ca14 Prevent people from starting with --sqlitefile or --mysql (#4772) 2023-03-20 10:40:48 +09:00
Nicolas Dorier
04ba1430ca Refactor plugin apps (#4780)
* Refactor plugins

* Add missing names to view models

* Cleanups

* Replace SalesAppBaseType by two interfaces

---------

Co-authored-by: Dennis Reimann <mail@dennisreimann.de>
2023-03-20 10:39:26 +09:00
d11n
53f3758abc Replace text in copy buttons with icon (#4764)
Closes #4699.
2023-03-19 21:43:38 +01:00
d11n
c6742f5533 Language selector: Ensure correct font-size (#4761)
* Language selector: Ensure correct font-size

Fixes the cut-off text on iOS, because somehow iOS uses a larger font-size by default.

* Fix select background color

Webkit-based browsers displayed transparent in a weird way.
2023-03-19 08:44:23 +01:00
d11n
cb44591a47 Derivation scheme parsing incorporates fingerprint and key path (#4781) 2023-03-17 14:35:30 +01:00
Wouter Samaey
e02abb509f Allow plugins to do something before and after automatic payouts (#4224) 2023-03-17 13:50:37 +01:00
Nicolas Dorier
eff6be9643 Remove mention of LNUrl-Withdraw when paying by NFC (#4779) 2023-03-17 12:24:27 +01:00
Andrew Camilleri
348dbd7107 Support Form Select option (#4726)
* Support Form Select option

* Add country select
2023-03-17 14:37:37 +09:00
Andrew Camilleri
f74ea14d8b Plugins can now build apps (#4608)
* Plugins can now build apps

* fix tests

* fixup

* pluginize existing apps

* Test fixes part 1

* Test fixes part 2

* Fix Crowdfund namespace

* Syntax

* More namespace fixes

* Markup

* Test fix

* upstream fixes

* Add plugin icon

* Fix nullable build warnings

* allow pre popualting app creation

* Fixes after merge

* Make link methods async

* Use AppData as parameter for ConfigureLink

* GetApps by AppType

* Use ConfigureLink on dashboard

* Rename method

* Add properties to indicate stats support

* Property updates

* Test fixes

* Clean up imports

* Fixes after merge

---------

Co-authored-by: Dennis Reimann <mail@dennisreimann.de>
2023-03-17 11:56:32 +09:00
d11n
a671632fde Dashboard: Fix app stats tiles (#4775)
* Dashboard: Fix app stats tiles

They broke with #4747, because they contain script blocks that are loaded asynchronuosly and need to get run once the chart data is added to the page.

* Refactor PoS dashboard component

* Collocate the component JS files in separate files

---------

Co-authored-by: nicolas.dorier <nicolas.dorier@gmail.com>
2023-03-16 15:51:24 +09:00
Andrew Camilleri
e344622c9e Form quick fixes (#4759) 2023-03-15 10:23:33 +01:00
Nicolas Dorier
06d7483ca3 Remove obsolete cli argument 'plugin-remote' (#4773) 2023-03-15 09:06:06 +01:00
nicolas.dorier
7fe041fc2c Changelog 1.8.4 2023-03-15 10:45:49 +09:00
nicolas.dorier
3f18e5476a Error when indexing invoices with some field that are too long (Fix #4771) 2023-03-15 09:31:38 +09:00
d11n
2a31613fe8 Fix invoice paid after expiration icon 2023-03-14 14:54:01 +01:00
d11n
ded0c8a3bc Update price display (#4736)
* Update price display

As proposed by @dstrukt in #4364.

* Update format

* Unify price display across the app

* Add DisplayFormatter

* Replace DisplayFormatCurrency method

* Use symbol currency format for invoice

* Unify currency formats on backend pages

* Revert recent changes

* Do not show exchange rate and fiat order amount for crypto denominations

* Fix test and add test cases
2023-03-13 10:12:58 +09:00
d11n
f3d9e07c5e Checkout v2: Celebrate payment with confetti (#4727)
* Checkout v2: Celebrate payment with confetti

Have a colorful celebration for successful payments.

* Make it default and add test
2023-03-13 10:09:56 +09:00
d11n
eb3ba95114 Make CanUsePullPaymentsViaUI more robust (#4762)
Fixes this nasty flaky test failure:

```
Failed CanUsePullPaymentsViaUI [17 s]
  Error Message:
   Assert.Equal() Failure
           ↓ (pos 1)
Expected: payout
Actual:   pull-payment
           ↑ (pos 1)
  Stack Trace:
     at BTCPayServer.Tests.ChromeTests.CanUsePullPaymentsViaUI() in /source/BTCPayServer.Tests/SeleniumTests.cs:line 1622
```

Because there are actually two labels, the previous selector was dependent on the correct ordern, because it always chose the first one …
2023-03-13 10:02:07 +09:00
Andrew Camilleri
7951dcada6 make sure we have cors for all of greenfield (#4760)
fixes #4758
2023-03-10 15:20:11 +01:00
Andrew Camilleri
06951a39c6 fix API breaking changefor payout processors (#4757)
fixes #4752
2023-03-10 17:57:33 +09:00
d11n
abe29f21f0 Checkout v2: Option to display amount in Sats in BIP21 case (#4730) 2023-03-09 21:36:11 +01:00
d11n
f57eab3008 Store branding: Add complementing text and accent colors (#4746) 2023-03-09 21:34:15 +01:00
nicolas.dorier
6d4b2348ac Update changelog 2023-03-08 21:56:40 +09:00
d11n
397ca6ef0c Checkout v2: Minor UI updates (#4734)
* Checkout v2: Minor copy change

* Allow copying of invoice ID and order ID on results page

* Add copy icons for payment details on results view

* Add missing powered by class to store footers
2023-03-08 21:39:03 +09:00
d11n
d6e5ee2851 UI: Decrease content padding top on small screens (#4749)
If the viewport height is less than 800px, decrease the content padding top for breakpoints L and on.
2023-03-08 21:37:25 +09:00
Nicolas Dorier
98d62e826b Do not through missing-permission error when no store on /api/v1/stores (Close #4735) (#4748) 2023-03-08 21:36:51 +09:00
Nicolas Dorier
7b5ce8f70c CSP: Remove unsafe-eval when vue isn't used (#4747)
* CSP: Remove unsafe-eval when vue isn't used

* Prevent XSS injection via VueJS
2023-03-08 17:57:36 +09:00
nicolas.dorier
2010a9a458 bump 2023-03-07 10:29:18 +09:00
nicolas.dorier
f787058c17 Fix: Impossible to create invoice after migration from Sqlite (Close #4743) 2023-03-07 10:27:04 +09:00
Kukks
87ccae0d90 add missing docs of store payment method criteria 2023-03-05 14:40:18 +01:00
nicolas.dorier
07d95c6ed7 bump clightning 2023-03-05 11:08:01 +09:00
nicolas.dorier
514823f7d2 bump clightning 2023-03-04 21:39:49 +09:00
d11n
fb4feb24f3 Minor updates from design repo (#4733) 2023-03-04 09:36:30 +01:00
Nicolas Dorier
5caa0e0722 [Greenfield] Allow passing email instead of user id in API (#4732) 2023-03-03 21:24:27 +09:00
nicolas.dorier
0406b420c8 Do not create if create API key is called on a non-existant user (Fix #4731) 2023-03-03 20:30:54 +09:00
nicolas.dorier
9d72b9779e Update Changelog 2023-03-03 20:20:59 +09:00
nicolas.dorier
fdc47e4a38 Avoid crash when some plugins are installed (#4725) 2023-03-03 20:18:09 +09:00
nicolas.dorier
0566e964c0 Fix incorrect punctuation in translations 2023-03-03 17:32:46 +09:00
nicolas.dorier
896fbf9a5c Lang update 2023-03-03 16:38:39 +09:00
d11n
126c8c101e Re-add language selector (#4723)
Fixed version
2023-03-02 16:34:15 +09:00
nicolas.dorier
3cb7cc01e4 Update db-migration.md 2023-03-02 13:20:47 +09:00
nicolas.dorier
2b3d15bf45 Update Changelog.md 2023-03-01 23:50:35 +09:00
d11n
4049bdadcb Changelog v1.8 (#4670)
* Changelog v1.8

* Update Changelog.md

Co-authored-by: Andrew Camilleri <evilkukka@gmail.com>

* Add fix

* Update changlelog

---------

Co-authored-by: Andrew Camilleri <evilkukka@gmail.com>
Co-authored-by: nicolas.dorier <nicolas.dorier@gmail.com>
2023-03-01 23:41:43 +09:00
d11n
2042ba37d8 Update What's New (#4713)
Links to https://blog.btcpayserver.org/btcpay-server-1-8-0/ which doesn't exist yet, that's why the linkcheck is expected to fail.
2023-03-01 23:41:27 +09:00
1493 changed files with 63941 additions and 104956 deletions

View File

@@ -31,79 +31,23 @@ jobs:
- run:
command: |
curl -X POST -H "Authorization: token $GH_PAT" -H "Accept: application/vnd.github.everest-preview+json" -H "Content-Type: application/json" https://api.github.com/repos/btcpayserver/btcpayserver-doc/dispatches --data '{"event_type": "build_docs"}'
# publish jobs require $DOCKERHUB_REPO, $DOCKERHUB_USER, $DOCKERHUB_PASS defined
amd64:
machine:
image: ubuntu-2004:202111-02
docker:
docker:
- image: cimg/base:stable
steps:
- setup_remote_docker
- checkout
- run:
command: |
LATEST_TAG=${CIRCLE_TAG:1} #trim v from tag
GIT_COMMIT=$(git rev-parse HEAD)
#
sudo docker build --build-arg GIT_COMMIT=${GIT_COMMIT} --pull -t $DOCKERHUB_REPO:$LATEST_TAG-amd64 -f amd64.Dockerfile .
sudo docker build --build-arg GIT_COMMIT=${GIT_COMMIT} --pull --build-arg CONFIGURATION_NAME=Altcoins-Release -t $DOCKERHUB_REPO:$LATEST_TAG-altcoins-amd64 -f amd64.Dockerfile .
sudo docker login --username=$DOCKERHUB_USER --password=$DOCKERHUB_PASS
sudo docker push $DOCKERHUB_REPO:$LATEST_TAG-amd64
sudo docker push $DOCKERHUB_REPO:$LATEST_TAG-altcoins-amd64
arm32v7:
machine:
image: ubuntu-2004:202111-02
steps:
- checkout
- run:
command: |
sudo docker run --rm --privileged multiarch/qemu-user-static:register --reset
LATEST_TAG=${CIRCLE_TAG:1} #trim v from tag
GIT_COMMIT=$(git rev-parse HEAD)
#
sudo docker build --build-arg GIT_COMMIT=${GIT_COMMIT} --pull -t $DOCKERHUB_REPO:$LATEST_TAG-arm32v7 -f arm32v7.Dockerfile .
sudo docker build --build-arg GIT_COMMIT=${GIT_COMMIT} --pull --build-arg CONFIGURATION_NAME=Altcoins-Release -t $DOCKERHUB_REPO:$LATEST_TAG-altcoins-arm32v7 -f arm32v7.Dockerfile .
sudo docker login --username=$DOCKERHUB_USER --password=$DOCKERHUB_PASS
sudo docker push $DOCKERHUB_REPO:$LATEST_TAG-arm32v7
sudo docker push $DOCKERHUB_REPO:$LATEST_TAG-altcoins-arm32v7
arm64v8:
machine:
image: ubuntu-2004:202111-02
steps:
- checkout
- run:
command: |
sudo docker run --rm --privileged multiarch/qemu-user-static:register --reset
LATEST_TAG=${CIRCLE_TAG:1} #trim v from tag
GIT_COMMIT=$(git rev-parse HEAD)
#
sudo docker build --build-arg GIT_COMMIT=${GIT_COMMIT} --pull -t $DOCKERHUB_REPO:$LATEST_TAG-arm64v8 -f arm64v8.Dockerfile .
sudo docker build --build-arg GIT_COMMIT=${GIT_COMMIT} --build-arg CONFIGURATION_NAME=Altcoins-Release --pull -t $DOCKERHUB_REPO:$LATEST_TAG-altcoins-arm64v8 -f arm64v8.Dockerfile .
sudo docker login --username=$DOCKERHUB_USER --password=$DOCKERHUB_PASS
sudo docker push $DOCKERHUB_REPO:$LATEST_TAG-arm64v8
sudo docker push $DOCKERHUB_REPO:$LATEST_TAG-altcoins-arm64v8
multiarch:
machine:
image: ubuntu-2004:202201-02
steps:
- run:
command: |
sudo docker login --username=$DOCKERHUB_USER --password=$DOCKERHUB_PASS
#
LATEST_TAG=${CIRCLE_TAG:1} #trim v from tag
sudo docker manifest create --amend $DOCKERHUB_REPO:$LATEST_TAG $DOCKERHUB_REPO:$LATEST_TAG-amd64 $DOCKERHUB_REPO:$LATEST_TAG-arm32v7 $DOCKERHUB_REPO:$LATEST_TAG-arm64v8
sudo docker manifest annotate $DOCKERHUB_REPO:$LATEST_TAG $DOCKERHUB_REPO:$LATEST_TAG-amd64 --os linux --arch amd64
sudo docker manifest annotate $DOCKERHUB_REPO:$LATEST_TAG $DOCKERHUB_REPO:$LATEST_TAG-arm32v7 --os linux --arch arm --variant v7
sudo docker manifest annotate $DOCKERHUB_REPO:$LATEST_TAG $DOCKERHUB_REPO:$LATEST_TAG-arm64v8 --os linux --arch arm64 --variant v8
sudo docker manifest push $DOCKERHUB_REPO:$LATEST_TAG -p
sudo docker manifest create --amend $DOCKERHUB_REPO:$LATEST_TAG-altcoins $DOCKERHUB_REPO:$LATEST_TAG-altcoins-amd64 $DOCKERHUB_REPO:$LATEST_TAG-altcoins-arm32v7 $DOCKERHUB_REPO:$LATEST_TAG-altcoins-arm64v8
sudo docker manifest annotate $DOCKERHUB_REPO:$LATEST_TAG-altcoins $DOCKERHUB_REPO:$LATEST_TAG-altcoins-amd64 --os linux --arch amd64
sudo docker manifest annotate $DOCKERHUB_REPO:$LATEST_TAG-altcoins $DOCKERHUB_REPO:$LATEST_TAG-altcoins-arm32v7 --os linux --arch arm --variant v7
sudo docker manifest annotate $DOCKERHUB_REPO:$LATEST_TAG-altcoins $DOCKERHUB_REPO:$LATEST_TAG-altcoins-arm64v8 --os linux --arch arm64 --variant v8
sudo docker manifest push $DOCKERHUB_REPO:$LATEST_TAG-altcoins -p
docker login --username=$DOCKERHUB_USER --password=$DOCKERHUB_PASS
docker buildx create --use
DOCKER_BUILDX_OPTS="--platform linux/amd64,linux/arm64,linux/arm/v7 --build-arg GIT_COMMIT=${GIT_COMMIT} --push"
docker buildx build $DOCKER_BUILDX_OPTS -t $DOCKERHUB_REPO:$LATEST_TAG .
docker buildx build $DOCKER_BUILDX_OPTS -t $DOCKERHUB_REPO:$LATEST_TAG-altcoins --build-arg CONFIGURATION_NAME=Altcoins-Release .
workflows:
version: 2
build_and_test:
@@ -120,7 +64,7 @@ workflows:
# only act on version tags
tags:
only: /(v[1-9]+(\.[0-9]+)*(-[a-z0-9-]+)?)|(v[a-z0-9-]+)/
- amd64:
- docker:
filters:
# ignore any commit on any branch by default
branches:
@@ -130,25 +74,3 @@ workflows:
# OR features on specific versions like v1.0.0.88-lndseedbackup-1
tags:
only: /(v[1-9]+(\.[0-9]+)*(-[a-z0-9-]+)?)|(v[a-z0-9-]+)/
- arm32v7:
filters:
branches:
ignore: /.*/
tags:
only: /(v[1-9]+(\.[0-9]+)*(-[a-z0-9-]+)?)|(v[a-z0-9-]+)/
- arm64v8:
filters:
branches:
ignore: /.*/
tags:
only: /(v[1-9]+(\.[0-9]+)*(-[a-z0-9-]+)?)|(v[a-z0-9-]+)/
- multiarch:
requires:
- amd64
- arm32v7
- arm64v8
filters:
branches:
ignore: /.*/
tags:
only: /(v[1-9]+(\.[0-9]+)*(-[a-z0-9-]+)?)|(v[a-z0-9-]+)/

View File

@@ -1,8 +1,14 @@
blank_issues_enabled: true
contact_links:
- name: 🚀 Discussions
url: https://github.com/btcpayserver/btcpayserver/discussions
about: Technical discussions, questions and feature requests
- name: 💡 Request a feature
url: https://github.com/btcpayserver/btcpayserver/discussions/categories/ideas-feature-requests
about: Submit a feature request or vote on ideas posted by others. Features with most upvotes become roadmap candidates
- name: 🧑‍💻 Ask a technical question
url: https://github.com/btcpayserver/btcpayserver/discussions/new?category=technical-support
about: If you're experiencing a technical problem post it to our community support forum
- name: 🔌 Report a problem with a plugin
url: https://github.com/btcpayserver/btcpayserver/discussions/new?category=plugins-integrations
about: Experiencing a problem with a third-party plugin? Post it here and we will tag their developers to assist
- name: 📝 Official Documentation
url: https://docs.btcpayserver.org
about: Check our documentation for answers to common questions

2
.github/codeql/codeql-config.yml vendored Normal file
View File

@@ -0,0 +1,2 @@
paths-ignore:
- 'BTCPayServer/wwwroot/vendor/**/*.js'

80
.github/workflows/codeql.yml vendored Normal file
View File

@@ -0,0 +1,80 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
# Allow running tests manually. Usefull if scan failure, or need to rescan before next scheduled date.
workflow_dispatch:
# We scan only on a schedule for now, can uncomment the following to scan on commit or PR merge later on if deemed appropriate.
# push:
# branches: [ "master" ]
# pull_request:
# branches: [ "master" ]
schedule:
# Scan every Monday 06:00 UTC.
- cron: '0 6 * * 1'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'javascript', 'csharp' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Use only 'java' to analyze code written in Java, Kotlin or both
# Use only 'javascript' to analyze code written in JavaScript, TypeScript or both
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
steps:
- name: Checkout repository
uses: actions/checkout@v3
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
config-file: ./.github/codeql/codeql-config.yml
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v2
# Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
# If the Autobuild fails above, remove it and uncomment the following three lines.
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
# - run: |
# echo "Run, Build Application using script"
# ./location_of_script_within_repo/buildscript.sh
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
with:
category: "/language:${{matrix.language}}"

4
.gitignore vendored
View File

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

View File

@@ -1,21 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Pack Test Plugin" type="DotNetProject" factoryName=".NET Project" singleton="false">
<option name="EXE_PATH" value="$PROJECT_DIR$/BTCPayServer.PluginPacker/bin/Debug/netcoreapp3.1/BTCPayServer.PluginPacker.dll" />
<option name="PROGRAM_PARAMETERS" value="../../../../BTCPayServer.Plugins.Test\bin\Debug\netcoreapp3.1 BTCPayServer.Plugins.Test &quot;../../../../Packed Plugins&quot;" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/BTCPayServer.PluginPacker/bin/Debug/netcoreapp3.1" />
<option name="PASS_PARENT_ENVS" value="1" />
<option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="USE_MONO" value="0" />
<option name="RUNTIME_ARGUMENTS" value="" />
<option name="PROJECT_PATH" value="$PROJECT_DIR$/BTCPayServer.PluginPacker/BTCPayServer.PluginPacker.csproj" />
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
<option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value=".NETCoreApp,Version=v3.1" />
<method v="2">
<option name="Build" default="false" projectName="BTCPayServer.Plugins.Test" projectPath="C:\Git\btcpayserver\Plugins\BTCPayServer.Plugins.Test\BTCPayServer.Plugins.Test.csproj" />
<option name="Build" />
</method>
</configuration>
</component>

2
.vscode/launch.json vendored
View File

@@ -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,

View File

@@ -31,11 +31,9 @@
<None Include="icon.png" Pack="true" PackagePath="\" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="HtmlSanitizer" Version="5.0.372" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.9" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.9" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.7" />
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="6.0.1" />
<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" />

View File

@@ -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)
{

View File

@@ -4,6 +4,8 @@ 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;
@@ -12,16 +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,46 +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);
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];
}
}
}

View File

@@ -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; }
}
}

View File

@@ -1,3 +1,4 @@
using System;
using System.Threading.Tasks;
namespace BTCPayServer.Abstractions.Contracts
@@ -6,5 +7,8 @@ namespace BTCPayServer.Abstractions.Contracts
{
Task ApplyAction(string hook, object args);
Task<object> ApplyFilter(string hook, object args);
event EventHandler<(string hook, object args)> ActionInvoked;
event EventHandler<(string hook, object args)> FilterInvoked;
}
}

View File

@@ -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;
}
}

View File

@@ -1,8 +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)
{
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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}")
{
}
}

View File

@@ -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)
{
}
}

View File

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

View File

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

View File

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

View File

@@ -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}")
{
}
}

View File

@@ -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.")
{
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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}.")
{
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}

View File

@@ -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);
}

View File

@@ -1,15 +0,0 @@
using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Abstractions.Custodians.Client;
using Newtonsoft.Json.Linq;
namespace BTCPayServer.Abstractions.Custodians;
public interface ICanWithdraw
{
public Task<WithdrawResult> WithdrawAsync(string paymentMethod, decimal amount, JObject config, CancellationToken cancellationToken);
public Task<WithdrawResult> GetWithdrawalInfoAsync(string paymentMethod, string withdrawalId, JObject config, CancellationToken cancellationToken);
public string[] GetWithdrawablePaymentMethods();
}

View File

@@ -1,26 +0,0 @@
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, string locale,
CancellationToken cancellationToken = default);
}

View File

@@ -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);
}
}

View File

@@ -7,6 +7,10 @@ namespace BTCPayServer.Abstractions.Extensions;
public static class GreenfieldExtensions
{
public static IActionResult UserNotFound(this ControllerBase ctrl)
{
return ctrl.CreateAPIError(404, "user-not-found", "The user was not found");
}
public static IActionResult CreateValidationError(this ControllerBase controller, ModelStateDictionary modelState)
{
return controller.UnprocessableEntity(modelState.ToGreenfieldValidationError());

View File

@@ -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(

View File

@@ -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
{
@@ -20,6 +20,15 @@ namespace BTCPayServer.Abstractions.Extensions
Relative
}
public static void SetBlazorAllowed(this ViewDataDictionary viewData, bool allowed)
{
viewData["BlazorAllowed"] = allowed;
}
public static bool IsBlazorAllowed(this ViewDataDictionary viewData)
{
return viewData["BlazorAllowed"] is not false;
}
public static void SetActivePage<T>(this ViewDataDictionary viewData, T activePage, string title = null, string activeId = null)
where T : IConvertible
{
@@ -46,50 +55,71 @@ 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 IsActiveCategory(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 IsActiveCategory<T>(this ViewDataDictionary viewData, T category, object id = null)
{
return IsActivePage(viewData, page.ToString(), page.GetType().ToString(), id);
return IsActiveCategory(viewData, category.ToString(), id);
}
public static string IsActivePage<T>(this ViewDataDictionary viewData, IEnumerable<T> pages, object id = null)
where T : IConvertible
public static bool IsActivePage(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 IsActivePage<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 IsActiveCategory(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 IsActivePage(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 IsActivePage(viewData, pages, id) ? ACTIVE_CLASS : null;
}
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)

View File

@@ -28,7 +28,7 @@ public class Field
public bool Constant;
// HTML5 compatible type string like "text", "textarea", "email", "password", etc. Each type is a class and may contain more fields (i.e. "select" would have options).
// HTML5 compatible type string like "text", "textarea", "email", "password", etc.
public string Type;
public static Field CreateFieldset()
@@ -52,10 +52,10 @@ public class Field
public string HelpText;
[JsonExtensionData] public IDictionary<string, JToken> AdditionalData { get; set; }
public List<Field> Fields { get; set; } = new ();
public List<Field> Fields { get; set; } = new();
// The field is considered "valid" if there are no validation errors
public List<string> ValidationErrors = new ();
public List<string> ValidationErrors = new();
public virtual bool IsValid()
{

View File

@@ -5,7 +5,6 @@ using System.Reflection;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Primitives;
using Newtonsoft.Json.Linq;
using Npgsql.Internal.TypeHandlers.GeometricHandlers;
namespace BTCPayServer.Abstractions.Form;
@@ -32,6 +31,8 @@ public class Form
// Are all the fields valid in the form?
public bool IsValid()
{
if (TopMessages?.Any(t => t.Type == AlertMessage.AlertMessageType.Danger) is true)
return false;
return Fields.Select(f => f.IsValid()).All(o => o);
}
@@ -50,7 +51,7 @@ public class Form
HashSet<string> nameReturned = new();
foreach (var f in GetAllFieldsCore(new List<string>(), Fields))
{
var fullName = string.Join('_', f.Path);
var fullName = string.Join('_', f.Path.Where(s => !string.IsNullOrEmpty(s)));
if (!nameReturned.Add(fullName))
continue;
yield return (fullName, f.Path, f.Field);
@@ -63,11 +64,10 @@ public class Form
HashSet<string> nameReturned = new();
foreach (var f in GetAllFieldsCore(new List<string>(), Fields))
{
var fullName = string.Join('_', f.Path);
var fullName = string.Join('_', f.Path.Where(s => !string.IsNullOrEmpty(s)));
if (!nameReturned.Add(fullName))
{
errors.Add($"Form contains duplicate field names '{fullName}'");
continue;
}
}
return errors.Count == 0;
@@ -84,15 +84,10 @@ public class Form
thisPath.Add(field.Name);
yield return (thisPath, field);
}
foreach (var child in field.Fields)
foreach (var descendant in GetAllFieldsCore(thisPath, field.Fields))
{
if (field.Constant)
child.Constant = true;
foreach (var descendant in GetAllFieldsCore(thisPath, field.Fields))
{
yield return descendant;
}
descendant.Field.Constant = field.Constant || descendant.Field.Constant;
yield return descendant;
}
}
}
@@ -109,51 +104,7 @@ public class Form
}
}
public void SetValues(JObject values)
{
var fields = GetAllFields().ToDictionary(k => k.FullName, k => k.Field);
SetValues(fields, new List<string>(), values);
}
private void SetValues(Dictionary<string, Field> fields, List<string> path, JObject values)
{
foreach (var prop in values.Properties())
{
List<string> propPath = new List<string>(path.Count + 1);
propPath.AddRange(path);
propPath.Add(prop.Name);
if (prop.Value.Type == JTokenType.Object)
{
SetValues(fields, propPath, (JObject)prop.Value);
}
else if (prop.Value.Type == JTokenType.String)
{
var fullname = String.Join('_', propPath);
if (fields.TryGetValue(fullname, out var f) && !f.Constant)
f.Value = prop.Value.Value<string>();
}
}
}
public JObject GetValues()
{
var r = new JObject();
foreach (var f in GetAllFields())
{
var node = r;
for (int i = 0; i < f.Path.Count - 1; i++)
{
var p = f.Path[i];
var child = node[p] as JObject;
if (child is null)
{
child = new JObject();
node[p] = child;
}
node = child;
}
node[f.Field.Name] = f.Field.Value;
}
return r;
}
}

View File

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

View File

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

View File

@@ -114,6 +114,11 @@ namespace BTCPayServer.Security
_Policies.Add(policy);
}
public void UnsafeEval()
{
Add("script-src", "'unsafe-eval'");
}
public IEnumerable<ConsentSecurityPolicy> Rules => _Policies;
public bool HasRules => _Policies.Count != 0;

View File

@@ -1,4 +1,5 @@
using Ganss.XSS;
using System.Web;
using Ganss.Xss;
using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Mvc.Rendering;
@@ -21,6 +22,11 @@ namespace BTCPayServer.Abstractions.Services
{
return _htmlHelper.Raw(_htmlSanitizer.Sanitize(value));
}
public IHtmlContent RawEncode(string value)
{
return _htmlHelper.Raw(HttpUtility.HtmlEncode(_htmlSanitizer.Sanitize(value)));
}
public IHtmlContent Json(object model)
{

View File

@@ -2,47 +2,93 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Razor.TagHelpers;
using Microsoft.Extensions.Logging;
using System;
using System.Linq;
namespace BTCPayServer.Abstractions.TagHelpers;
[HtmlTargetElement(Attributes = nameof(Permission))]
[HtmlTargetElement(Attributes = "[permission]")]
[HtmlTargetElement(Attributes = "[not-permission]")]
public class PermissionTagHelper : TagHelper
{
private readonly IAuthorizationService _authorizationService;
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly ILogger<PermissionTagHelper> _logger;
public PermissionTagHelper(IAuthorizationService authorizationService, IHttpContextAccessor httpContextAccessor, ILogger<PermissionTagHelper> logger)
public PermissionTagHelper(IAuthorizationService authorizationService, IHttpContextAccessor httpContextAccessor)
{
_authorizationService = authorizationService;
_httpContextAccessor = httpContextAccessor;
_logger = logger;
}
public string Permission { get; set; }
public string NotPermission { get; set; }
public string PermissionResource { get; set; }
public bool AndMode { get; set; } = false;
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
if (string.IsNullOrEmpty(Permission))
var permissions = Permission?.Split(',', StringSplitOptions.RemoveEmptyEntries) ?? Array.Empty<string>();
var notPermissions = NotPermission?.Split(',', StringSplitOptions.RemoveEmptyEntries) ?? Array.Empty<string>();
if (!permissions.Any() && !notPermissions.Any())
return;
if (_httpContextAccessor.HttpContext is null)
return;
var key = $"{Permission}_{PermissionResource}";
if (!_httpContextAccessor.HttpContext.Items.TryGetValue(key, out var o) ||
o is not AuthorizationResult res)
bool shouldRender = true; // Assume tag should be rendered unless a check fails
// Process 'Permission' - User must have these permissions
if (permissions.Any())
{
res = await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User,
PermissionResource,
Permission);
_httpContextAccessor.HttpContext.Items.Add(key, res);
bool finalResult = AndMode;
foreach (var perm in permissions)
{
var key = $"{perm}_{PermissionResource}";
AuthorizationResult res = await GetOrAddAuthorizationResult(key, perm);
if (AndMode)
finalResult &= res.Succeeded;
else
finalResult |= res.Succeeded;
if (!AndMode && finalResult) break;
}
shouldRender = finalResult;
}
if (!res.Succeeded)
// Process 'NotPermission' - User must not have these permissions
if (shouldRender && notPermissions.Any())
{
foreach (var notPerm in notPermissions)
{
var key = $"{notPerm}_{PermissionResource}";
AuthorizationResult res = await GetOrAddAuthorizationResult(key, notPerm);
if (res.Succeeded) // If the user has a 'NotPermission', they should not see the tag
{
shouldRender = false;
break;
}
}
}
if (!shouldRender)
{
output.SuppressOutput();
}
}
private async Task<AuthorizationResult> GetOrAddAuthorizationResult(string key, string permission)
{
if (!_httpContextAccessor.HttpContext.Items.TryGetValue(key, out var cachedResult))
{
var res = await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User,
PermissionResource, permission);
_httpContextAccessor.HttpContext.Items[key] = res;
return res;
}
return cachedResult as AuthorizationResult;
}
}

View File

@@ -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();
}

View File

@@ -23,12 +23,12 @@ public class SVGUse : UrlResolutionTagHelper2
{
_fileVersionProvider = fileVersionProvider;
}
public override void Process(TagHelperContext context, TagHelperOutput output)
{
var attr = output.Attributes["href"].Value.ToString();
var symbolIndex = attr!.IndexOf("#", StringComparison.InvariantCulture);
var start = attr.IndexOf("~", StringComparison.InvariantCulture) + 1;
var start = attr.IndexOf("~", StringComparison.InvariantCulture) + 1;
var length = (symbolIndex != -1 ? symbolIndex : attr.Length) - start;
var filePath = attr.Substring(start, length);
if (!string.IsNullOrEmpty(filePath))

View File

@@ -12,9 +12,11 @@
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<RepositoryUrl>https://github.com/btcpayserver/btcpayserver</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<Configurations>Debug;Release</Configurations>
<Platforms>AnyCPU</Platforms>
</PropertyGroup>
<PropertyGroup>
<Version Condition=" '$(Version)' == '' ">1.7.2</Version>
<Version Condition=" '$(Version)' == '' ">1.7.4</Version>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<PublishRepositoryUrl>true</PublishRepositoryUrl>
@@ -28,9 +30,9 @@
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="BTCPayServer.Lightning.Common" Version="1.3.21" />
<PackageReference Include="NBitcoin" Version="7.0.24" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="BTCPayServer.Lightning.Common" Version="1.5.1" />
<PackageReference Include="NBitcoin" Version="7.0.37" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>
<ItemGroup>
<None Include="icon.png" Pack="true" PackagePath="\" />

View File

@@ -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);
}
}

View File

@@ -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<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(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(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<AppBaseData[]> GetAllApps(CancellationToken token = default)
{
return await SendHttpRequest<AppBaseData[]>("api/v1/apps", 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<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<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<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 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<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<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);
}
}

View File

@@ -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;
}
}

View File

@@ -1,97 +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> GetDepositAddress(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> GetTradeInfo(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> GetTradeQuote(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> CreateWithdrawal(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<WithdrawalResponseData> GetWithdrawalInfo(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);
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}
}

View File

@@ -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)
{
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/server/lightning/{cryptoCode}/info",
method: HttpMethod.Get), token);
return await HandleResponse<LightningNodeInformationData>(response);
}
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)
{
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/server/lightning/{cryptoCode}/balance",
method: HttpMethod.Get), token);
return await HandleResponse<LightningNodeBalanceData>(response);
}
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));
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/server/lightning/{cryptoCode}/connect", bodyPayload: request,
method: HttpMethod.Post), token);
await HandleResponse(response);
}
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)
{
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/server/lightning/{cryptoCode}/channels",
method: HttpMethod.Get), token);
return await HandleResponse<IEnumerable<LightningChannelData>>(response);
}
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)
{
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 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)
{
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<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));
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> 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));
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<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));
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> 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)
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 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);
queryPayload.Add("pendingOnly", v.ToString());
}
public virtual async Task<LightningPaymentData[]> GetLightningPayments(string cryptoCode,
bool? includePending = null, long? offsetIndex = null, CancellationToken token = default)
if (offsetIndex is > 0)
{
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);
queryPayload.Add("offsetIndex", offsetIndex);
}
return await SendHttpRequest<LightningInvoiceData[]>($"api/v1/server/lightning/{cryptoCode}/invoices", queryPayload, HttpMethod.Get, token);
}
public virtual async Task<LightningInvoiceData> CreateLightningInvoice(string cryptoCode, CreateLightningInvoiceRequest 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}/invoices", bodyPayload: request,
method: HttpMethod.Post), token);
return await HandleResponse<LightningInvoiceData>(response);
queryPayload.Add("includePending", v.ToString());
}
if (offsetIndex is > 0)
{
queryPayload.Add("offsetIndex", offsetIndex);
}
return await SendHttpRequest<LightningPaymentData[]>($"api/v1/server/lightning/{cryptoCode}/payments", queryPayload, HttpMethod.Get, token);
}
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);
}
}

View File

@@ -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)
{
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/lightning/{cryptoCode}/info",
method: HttpMethod.Get), token);
return await HandleResponse<LightningNodeInformationData>(response);
}
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)
{
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/lightning/{cryptoCode}/balance",
method: HttpMethod.Get), token);
return await HandleResponse<LightningNodeBalanceData>(response);
}
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));
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/lightning/{cryptoCode}/connect", bodyPayload: request,
method: HttpMethod.Post), token);
await HandleResponse(response);
}
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)
{
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/lightning/{cryptoCode}/channels",
method: HttpMethod.Get), token);
return await HandleResponse<IEnumerable<LightningChannelData>>(response);
}
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)
{
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 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)
{
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<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));
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> 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));
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<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));
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> 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)
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 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);
queryPayload.Add("pendingOnly", v.ToString());
}
public virtual async Task<LightningPaymentData[]> GetLightningPayments(string storeId, string cryptoCode,
bool? includePending = null, long? offsetIndex = null, CancellationToken token = default)
if (offsetIndex is > 0)
{
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);
queryPayload.Add("offsetIndex", offsetIndex);
}
return await SendHttpRequest<LightningInvoiceData[]>($"api/v1/stores/{storeId}/lightning/{cryptoCode}/invoices", queryPayload, HttpMethod.Get, token);
}
public virtual async Task<LightningInvoiceData> CreateLightningInvoice(string storeId, string cryptoCode,
CreateLightningInvoiceRequest 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}/invoices", bodyPayload: request,
method: HttpMethod.Post), token);
return await HandleResponse<LightningInvoiceData>(response);
queryPayload.Add("includePending", v.ToString());
}
if (offsetIndex is > 0)
{
queryPayload.Add("offsetIndex", offsetIndex);
}
return await SendHttpRequest<LightningPaymentData[]>($"api/v1/stores/{storeId}/lightning/{cryptoCode}/payments", queryPayload, HttpMethod.Get, token);
}
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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -7,134 +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,
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);
}
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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -5,107 +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<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<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 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<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<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<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 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 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<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[]> 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<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);
}
}

View File

@@ -1,15 +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)
{
return await SendHttpRequest<List<RoleData>>("api/v1/server/roles", null, HttpMethod.Get, token);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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? paymentMethod = null, CancellationToken token = default)
{
return await SendHttpRequest<IEnumerable<LightningAutomatedPayoutSettings>>($"api/v1/stores/{storeId}/payout-processors/LightningAutomatedPayoutSenderFactory{(paymentMethod is null ? string.Empty : $"/{paymentMethod}")}", null, HttpMethod.Get, token);
}
public virtual async Task<LightningAutomatedPayoutSettings> UpdateStoreLightningAutomatedPayoutProcessors(string storeId, string paymentMethod, LightningAutomatedPayoutSettings request, CancellationToken token = default)
{
return await SendHttpRequest<LightningAutomatedPayoutSettings>($"api/v1/stores/{storeId}/payout-processors/LightningAutomatedPayoutSenderFactory/{paymentMethod}", 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);
}
}

View File

@@ -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);
}
}

View File

@@ -5,33 +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<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);
}
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)
{
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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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,153 +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 err = JsonConvert.DeserializeObject<Models.GreenfieldValidationError[]>(await message.Content.ReadAsStringAsync());
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);
}
}
}

View File

@@ -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; }
}

View File

@@ -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; }
}

View File

@@ -30,9 +30,9 @@ namespace BTCPayServer.JsonConverters
case JTokenType.Integer:
case JTokenType.String:
if (objectType == typeof(decimal) || objectType == typeof(decimal?))
return decimal.Parse(token.ToString(), CultureInfo.InvariantCulture);
return decimal.Parse(token.ToString(), NumberStyles.Any, CultureInfo.InvariantCulture);
if (objectType == typeof(double) || objectType == typeof(double?))
return double.Parse(token.ToString(), CultureInfo.InvariantCulture);
return double.Parse(token.ToString(), NumberStyles.Any, CultureInfo.InvariantCulture);
throw new JsonSerializationException("Unexpected object type: " + objectType);
case JTokenType.Null when objectType == typeof(decimal?) || objectType == typeof(double?):
return null;

View File

@@ -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());
}
}
}

View File

@@ -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);

View File

@@ -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; }
}

View File

@@ -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; }
}

View File

@@ -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; }
}

View File

@@ -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>

View File

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

View File

@@ -1,84 +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; } = true;
public bool EnableTips { get; set; } = true;
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 string FormId { get; set; } = null;
public string EmbeddedCSS { get; set; } = null;
public CheckoutType? CheckoutType { 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 string[] Sounds { get; set; } = null;
public string[] AnimationColors { get; set; } = null;
}
}

View File

@@ -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>

View File

@@ -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; }
}
}

View File

@@ -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")]

View File

@@ -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; }
}
}

View File

@@ -1,8 +1,11 @@
#nullable enable
using Newtonsoft.Json.Linq;
namespace BTCPayServer.Client.Models;
public class CreatePayoutThroughStoreRequest : CreatePayoutRequest
{
public string? PullPaymentId { get; set; }
public bool? Approved { get; set; }
public JObject? Metadata { get; set; }
}

View File

@@ -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; }
}
}

View File

@@ -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
}

View File

@@ -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; }
}
}

View File

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

View File

@@ -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()
{
}
}

View File

@@ -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; }
}

View File

@@ -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; }
}

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