Compare commits

..

11 Commits
master ... 3.x

Author SHA1 Message Date
utxo.one
81e1b17f10
Store Chain Wallet Tests + Getters (#119)
Some checks failed
Code Style / php-cs-fixer (push) Has been cancelled
Static analysis (Psalm) / Psalm (8.0) (push) Has been cancelled
* Store Chain Wallet Tests + Getters
2023-08-01 17:05:39 +02:00
utxo.one
c6876af1d0
StoreEmail Tests & Client Fixes (#117)
* StoreEmail Tests & Client Fixes

* remove phpunit tests
2023-07-04 18:30:21 +02:00
utxo.one
75451c6ba3
StoreClient Tests + Result Updates (#116)
* Store Tests
* removed unused variable
* remove commented methods
2023-06-26 22:07:49 +02:00
utxo.one
3e6ab646fa
ServerInfo Test + Result Getters (#115)
* ServerSyncList Class, PreciseNumber fix
2023-06-24 23:33:17 +02:00
utxo.one
ff667252d8
3.x Pull Payment Test Fix (#114)
* remove duplicate function, fix pull payment test
* cs fix
2023-06-24 22:39:09 +02:00
utxo.one
a3f4a60400 Dev 3.x merge new prs (#111)
* Get v3 up to date with latest PRs on v2
2023-05-07 00:21:11 +02:00
utxo.one
90c6ff52d3 Pull Payments Test (#92) 2023-05-07 00:18:16 +02:00
utxo.one
bb8e7e8bf0 Notification Tests (#90)
* Notification Tests
2023-05-07 00:18:16 +02:00
utxo.one
5d19edcff8 Miscellaneous Client Test Suite (#88)
* Miscellaneous Client Test Suite
* InvoiceCheckoutHtml Result Class + Test
2023-05-07 00:18:08 +02:00
utxo.one
261bb459a3 LightningStore Client Test Suite (#86)
* LightningStore Client Test Suite
* Renames getInvoice to getLightningInvoice
* Adds amount, maxFeePercent and maxFeeFlat to payLightningInvoice
2023-05-07 00:16:59 +02:00
utxo.one
158ab833f4 Complete Invoice Client/Result Classes & Test Suite (#82)
* Extending Invoice Client and Result classes

* Health and Invoice Tests

* Removed isPaid

* Move requiresRefundEmail to bottom with default value
2023-05-07 00:16:59 +02:00
75 changed files with 1823 additions and 1842 deletions

View File

@ -8,7 +8,7 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v5
uses: actions/checkout@v3
- name: Run PHP CS Fixer
uses: docker://oskarstark/php-cs-fixer-ga

View File

@ -1,53 +1,52 @@
#name: PHP Unit Tests
#env:
# BTCPAY_HOST: ${{ secrets.BTCPAY_HOST }}
# BTCPAY_API_KEY: ${{ secrets.BTCPAY_API_KEY }}
# BTCPAY_STORE_ID: ${{ secrets.BTCPAY_STORE_ID }}
# BTCPAY_NODE_URI: ${{ secrets.BTCPAY_NODE_URI }}
#on: [ push, pull_request ]
#
#jobs:
# phpunit:
# runs-on: ubuntu-latest
# strategy:
# matrix:
# php-versions: ['8.0', '8.1']
# phpunit-versions: ['latest']
#
#
# steps:
# - name: Checkout
# uses: actions/checkout@v3
# with:
# fetch-depth: '0'
#
# - name: Setup PHP, with composer and extensions
# uses: shivammathur/setup-php@v2
# with:
# php-version: ${{ matrix.php-versions }}
# tools: composer:v2, phpunit:${{ matrix.phpunit-versions }}
# extensions: curl, json, mbstring, bcmath
# coverage: xdebug #optional
#
# - name: Get composer cache directory
# id: composer-cache
# run: echo "::set-output name=dir::$(composer config cache-files-dir)"
#
# - name: Cache composer dependencies
# uses: actions/cache@v2
# with:
# path: ${{ steps.composer-cache.outputs.dir }}
# key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
# restore-keys: ${{ runner.os }}-composer-
#
# - name: Install Composer dependencies
# run: composer install --no-progress --optimize-autoloader
#
# - name: Test with phpunit
# run: vendor/bin/phpunit --coverage-text
#
# - name: Setup problem matchers for PHP
# run: echo "::add-matcher::${{ runner.tool_cache }}/php.json"
#
# - name: Setup problem matchers for PHPUnit
# run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"
# name: PHP Unit Tests
# env:
# BTCPAY_HOST: ${{ secrets.BTCPAY_HOST }}
# BTCPAY_API_KEY: ${{ secrets.BTCPAY_API_KEY }}
# BTCPAY_STORE_ID: ${{ secrets.BTCPAY_STORE_ID }}
# BTCPAY_NODE_URI: ${{ secrets.BTCPAY_NODE_URI }}
# on: [push, pull_request]
# jobs:
# phpunit:
# runs-on: ubuntu-latest
# strategy:
# matrix:
# php-versions: ["8.0", "8.1"]
# phpunit-versions: ["latest"]
# steps:
# - name: Checkout
# uses: actions/checkout@v3
# with:
# fetch-depth: "0"
# - name: Setup PHP, with composer and extensions
# uses: shivammathur/setup-php@v2
# with:
# php-version: ${{ matrix.php-versions }}
# tools: composer:v2, phpunit:${{ matrix.phpunit-versions }}
# extensions: curl, json, mbstring, bcmath
# coverage: xdebug #optional
# - name: Get composer cache directory
# id: composer-cache
# run: echo "::set-output name=dir::$(composer config cache-files-dir)"
# - name: Cache composer dependencies
# uses: actions/cache@v2
# with:
# path: ${{ steps.composer-cache.outputs.dir }}
# key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
# restore-keys: ${{ runner.os }}-composer-
# - name: Install Composer dependencies
# run: composer install --no-progress --optimize-autoloader
# - name: Test with phpunit
# run: vendor/bin/phpunit --coverage-text
# - name: Setup problem matchers for PHP
# run: echo "::add-matcher::${{ runner.tool_cache }}/php.json"
# - name: Setup problem matchers for PHPUnit
# run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"

View File

@ -9,12 +9,12 @@ jobs:
strategy:
fail-fast: false
matrix:
php-versions: ['8.0']
php-versions: ["8.0"]
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v3
with:
fetch-depth: '0'
fetch-depth: "0"
- name: Setup PHP, with composer and extensions
uses: shivammathur/setup-php@v2
@ -27,7 +27,7 @@ jobs:
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
- name: Cache composer dependencies
uses: actions/cache@v5
uses: actions/cache@v2
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}

3
.gitignore vendored
View File

@ -4,4 +4,5 @@
*.cache
composer.lock
/tests/.env
/.claude/
/tests/.env.testnet
/tests/.env.mainnet

View File

@ -1,11 +1,14 @@
# BTCPay Server Greenfield API PHP client library
This library makes it easier to integrate BTCPay Server in your PHP application.
## Approach
This library takes an opinionated approach to Greenfield API with the aim of making your developer life as easy and convenient as possible.
For this reason, we have decided to structure arguments a bit differently, but still allow full and advanced use cases.
The general reasoning behind the arguments an API client takes are in this order:
- First the required parameters => method arguments with NULL not allowed
- Recommended parameters => method arguments with NULL as default
- Optional parameters => arguments with NULL as default
@ -14,11 +17,13 @@ The general reasoning behind the arguments an API client takes are in this order
Methods that return a Unix timestamp always end with `Timestamp` like `getReceivedTimestamp()` to avoid format and timezone confusion. These are always in seconds (not milliseconds).
## Features
- No external dependencies. You can just drop this code in your project using composer or without composer.
- Requires PHP 8.0 and up. End-of-life'd versions will not be actively supported.
- All calls needed for eCommerce are included, but there are more we still need to add.
## TODO
- convert examples to tests
- Getters and setters
- Expand beyond the eCommerce related API calls and make this library 100% complete.
@ -28,13 +33,17 @@ Methods that return a Unix timestamp always end with `Timestamp` like `getReceiv
```
composer require btcpayserver/btcpayserver-greenfield-php
```
If you use some framework or other project you likely are ready to go. If you start from scratch make sure to include Composer autoloader.
```
require __DIR__ . '/../vendor/autoload.php';
```
## How to use without composer (not recommended)
In the `src` directory we have a custom `autoload.php` which you can require and avoid using composer if needed.
```
// Require the autoload file.
require __DIR__ . '/../src/autoload.php';
@ -52,19 +61,25 @@ try {
```
## Best practices
- Always use an API key with as little permissions as possible.
- If you only interact with specific stores, use an API key that is limited to that store or those stores only.
- When processing an incoming webhook, always load the data fresh using the API as the data may be stale or changed in the meantime. Webhook payloads can be resent on error, so you could be seeing outdated information. By loading the data fresh, you are also protecting yourself from possibly spoofed (fake) requests.
- When processing an incoming webhook, always load the data fresh using the API as the data may be stale or changed in the meantime. Webhook payloads can be resent on error, so you could be seeing outdated information. By loading the data fresh, you are also protecting yourself from possibly spoofed (fake) requests.
## FAQ
### Where to get the API key from?
The API keys for Greenfield API are *not* on the store level "Access Tokens" anymore. You need to go to your account profile: "My Settings" (user profile icon) -> "API Keys" instead. You can even redirect the users to generate the API keys there.
The API keys for Greenfield API are _not_ on the store level "Access Tokens" anymore. You need to go to your account profile: "My Settings" (user profile icon) -> "API Keys" instead. You can even redirect the users to generate the API keys there.
## Contribute
We run static analyzer [Psalm](https://psalm.dev/) and [PHP-CS-fixer](https://github.com/FriendsOfPhp/PHP-CS-Fixer) for codestyle when you open a pull-request. Please check if there are any errors and fix them accordingly.
### Codestyle
We use PSR-12 code style to ensure proper formatting and spacing. You can test and format your code using composer commands. Before doing a PR you can run `composer cs-check` and `composer cs-fix` which will run php-cs-fixer.
### Greenfield API coverage
Currently implemented functionality is tracked in [this sheet](https://docs.google.com/spreadsheets/d/1A1tMWYHGVkFWRgqfkW9GSGBRjzKZzsu5XMIW1NLs-xg/edit#gid=0) and will be updated sporadically. Check to see which areas still need work in case you want to contribute.

View File

@ -28,11 +28,17 @@
"friendsofphp/php-cs-fixer": "^3.0",
"vimeo/psalm": "^4.8",
"phpunit/phpunit": "^9.5",
"vlucas/phpdotenv": "^5.5"
"vlucas/phpdotenv": "^5.5",
"pestphp/pest": "^1.22"
},
"scripts": {
"cs-check": [ "vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.dist.php --allow-risky=yes --using-cache=no --verbose --dry-run" ],
"cs-fix": [ "vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.dist.php --allow-risky=yes --using-cache=no" ],
"psalm": [ "vendor/bin/psalm" ]
},
"config": {
"allow-plugins": {
"pestphp/pest-plugin": true
}
}
}

View File

@ -8,10 +8,9 @@ use BTCPayServer\Client\Store;
$apiKey = '';
$host = ''; // e.g. https://your.btcpay-server.tld
$storeId = '';
$updateStoreId = '';
$invoiceId = '';
// Get information about store on BTCPay Server.
try {
$client = new Store($host, $apiKey);
var_dump($client->getStore($storeId));
@ -22,17 +21,7 @@ try {
// Create a new store.
try {
$client = new Store($host, $apiKey);
$newStore = $client->createStore('New store', null, 'EUR');
var_dump($newStore);
} catch (\Throwable $e) {
echo "Error: " . $e->getMessage();
}
// Update a store.
// You need to pass all variables to make sure it does not get reset to defaults if you want to preserve them.
try {
$client = new Store($host, $apiKey);
var_dump($client->updateStore($updateStoreId, 'Store name CHANGED'));
var_dump($client->createStore('my new store'));
} catch (\Throwable $e) {
echo "Error: " . $e->getMessage();
}

View File

@ -22,22 +22,3 @@ try {
} catch (\Throwable $e) {
echo "Error: " . $e->getMessage();
}
// Get 2 invoices, skip 2
try {
echo 'Get invoices:' . PHP_EOL;
$client = new Invoice($host, $apiKey);
var_dump($client->getAllInvoices($storeId, 2, 2));
} catch (\Throwable $e) {
echo "Error: " . $e->getMessage();
}
// Get newer/equal than 2024-10-20
try {
echo 'Get invoices newer/equal than 2024-10-20:' . PHP_EOL;
$date = new DateTime('2024-10-20');
$client = new Invoice($host, $apiKey);
var_dump($client->getAllInvoicesWithFilter($storeId, null, null, null, $date->getTimestamp()));
} catch (\Throwable $e) {
echo "Error: " . $e->getMessage();
}

View File

@ -43,7 +43,7 @@ class PullPayments
$autoApproveClaims = false;
$startsAt = null;
$expiresAt = null;
$paymentMethods = ['BTC-CHAIN'];
$paymentMethods = ['BTC'];
try {
$client = new PullPayment($this->host, $this->apiKey);
@ -113,7 +113,7 @@ class PullPayments
public function approvePayout()
{
$payoutId = '';
$payoutId ='';
try {
$client = new PullPayment($this->host, $this->apiKey);
var_dump($client->approvePayout(
@ -163,7 +163,7 @@ class PullPayments
$pullPaymentId = '';
$destination = '';
$amount = PreciseNumber::parseString('0.000001');
$paymentMethod = 'BTC-CHAIN';
$paymentMethod = '';
try {
$client = new PullPayment($this->host, $this->apiKey);

View File

@ -32,21 +32,6 @@ class StoreOnChainWallets
}
}
public function createStoreOnChainWallet()
{
$cryptoCode = 'BTC';
try {
$client = new StoreOnChainWallet($this->host, $this->apiKey);
var_dump($client->createStoreOnchainWallet(
$this->storeId,
$cryptoCode
));
} catch (\Throwable $e) {
echo "Error: " . $e->getMessage();
}
}
public function getStoreOnChainWalletFeeRate()
{
$cryptoCode = 'BTC';
@ -186,4 +171,3 @@ $store = new StoreOnChainWallets();
$store->getStoreOnChainWalletTransactions();
//$store->getStoreOnChainWalletTransaction();
//$store->getStoreOnChainWalletUTXOs();
//$store->createStoreOnChainWallet();

View File

@ -1,242 +0,0 @@
<?php
// Include autoload file.
require __DIR__ . '/../vendor/autoload.php';
// Import Subscriptions client class.
use BTCPayServer\Client\Subscriptions;
// Fill in with your BTCPay Server data.
$apiKey = '';
$host = ''; // e.g. https://your.btcpay-server.tld
$storeId = '';
// Create the subscriptions client.
try {
$client = new Subscriptions($host, $apiKey);
echo "=== BTCPay Server Subscriptions API Examples ===\n\n";
// 1. Create a new offering
echo "1. Creating a new offering...\n";
$offering = $client->createOffering(
$storeId,
'Premium SaaS App',
'https://example.com/success',
[
'category' => 'saas',
'region' => 'us',
'version' => '1.0'
],
[
['id' => 'feature-analytics', 'description' => 'Advanced analytics dashboard'],
['id' => 'feature-support', 'description' => '24/7 priority support'],
['id' => 'feature-api', 'description' => 'Unlimited API access']
]
);
echo "Offering created with ID: " . $offering->getId() . "\n";
echo "App Name: " . $offering->getAppName() . "\n\n";
$offeringId = $offering->getId();
// 2. Create plans for the offering
echo "2. Creating plans for the offering...\n";
// Basic plan
$basicPlan = $client->createOfferingPlan(
$storeId,
$offeringId,
'Basic monthly subscription with essential features',
'USD',
7,
'Basic Plan',
true,
'1.99',
true,
null,
['tier' => 'basic'],
'Monthly',
['feature-analytics']
);
echo "Basic plan created with ID: " . $basicPlan->getId() . "\n";
// Premium plan
$premiumPlan = $client->createOfferingPlan(
$storeId,
$offeringId,
'Premium monthly subscription with all features',
'USD',
7,
'Premium Plan',
true,
'29.99',
true,
14,
['tier' => 'premium'],
'Monthly',
['feature-analytics', 'feature-support', 'feature-api']
);
echo "Premium plan created with ID: " . $premiumPlan->getId() . "\n\n";
$basicPlanId = $basicPlan->getId();
$premiumPlanId = $premiumPlan->getId();
// 2b. Update the offering
echo "2b. Updating the offering...\n";
$updatedOffering = $client->updateOffering(
$storeId,
$offeringId,
'Premium SaaS App v2',
'https://example.com/success-v2',
['category' => 'saas', 'region' => 'eu', 'version' => '2.0']
);
echo "Offering updated: " . $updatedOffering->getAppName() . "\n\n";
// 2c. Update a plan
echo "2c. Updating the basic plan...\n";
$updatedPlan = $client->updateOfferingPlan(
$storeId,
$offeringId,
$basicPlanId,
'Updated basic monthly subscription',
'USD',
7,
'Basic Plan v2',
null,
'2.99'
);
echo "Plan updated: " . $updatedPlan->getName() . " - " . $updatedPlan->getPrice() . " " . $updatedPlan->getCurrency() . "\n\n";
// 3. Get all offerings for the store
echo "3. Getting all offerings for the store...\n";
$offerings = $client->getOfferings($storeId);
foreach ($offerings->all() as $off) {
echo "- Offering: " . $off->getAppName() . " (ID: " . $off->getId() . ")\n";
foreach ($off->getPlans() as $plan) {
echo " - Plan: " . $plan->getName() . " - " . $plan->getPrice() . " " . $plan->getCurrency() . "/" . $plan->getRecurringType() . "\n";
}
}
echo "\n";
// 4. Get a specific offering
echo "4. Getting specific offering details...\n";
$specificOffering = $client->getOffering($storeId, $offeringId);
echo "Offering: " . $specificOffering->getAppName() . "\n";
echo "Success URL: " . $specificOffering->getSuccessRedirectUrl() . "\n";
echo "Number of plans: " . count($specificOffering->getPlans()) . "\n\n";
// 5. Get a specific plan
echo "5. Getting specific plan details...\n";
$specificPlan = $client->getOfferingPlan($storeId, $offeringId, $basicPlanId);
echo "Plan: " . $specificPlan->getName() . "\n";
echo "Price: " . $specificPlan->getPrice() . " " . $specificPlan->getCurrency() . "\n";
echo "Trial Days: " . $specificPlan->getTrialDays() . "\n";
echo "Features: " . implode(', ', $specificPlan->getFeatures()) . "\n\n";
// 6. Create a plan checkout session
echo "6. Creating a plan checkout session...\n";
$checkout = $client->createPlanCheckout(
$storeId,
$offeringId,
$basicPlanId,
null, // If the customer already exists on BTCPay, fill the email or other id here.
60,
'SoftMigration',
['source' => 'web'],
['campaign' => 'summer2026'],
['flow' => 'new_signup'],
false,
null, // You can override the plan price here if you want to force more credit or custom amount.
'https://example.com/welcome',
'test@example.com' // This is optional and will prefill the checkout page with the email.
);
echo "Checkout created with ID: " . $checkout->getId() . "\n";
echo "Checkout URL: " . $checkout->getUrl() . "\n";
echo "Is Trial: " . ($checkout->isTrial() ? 'Yes' : 'No') . "\n";
echo "New Subscriber: " . ($checkout->isNewSubscriber() ? 'Yes' : 'No') . "\n\n";
$checkoutId = $checkout->getId();
// 7. Get plan checkout details
echo "7. Getting plan checkout details...\n";
$checkoutDetails = $client->getPlanCheckout($checkoutId);
echo "Checkout ID: " . $checkoutDetails->getId() . "\n";
echo "Plan: " . $checkoutDetails->getPlan()->getName() . "\n";
$subscriber = $checkoutDetails->getSubscriber();
if ($subscriber && $subscriber->getCustomer()->getIdentities()) {
echo "Subscriber Email: " . ($subscriber->getCustomer()->getIdentities()['Email'] ?? 'N/A') . "\n";
}
echo "\n";
// 8. Subscriber management examples
/*
// Fill these variables with actual values to test subscriber operations
$offeringId = ''; // e.g. "offering_GFbMSBpybM6i5uEiqc"
$customerSelector = ''; // e.g. "ps_N71XxcPDnKNgNDxKHZ" or customer email
$suspensionReason = 'User requested cancellation';
if (!empty($storeId) && !empty($offeringId) && !empty($customerSelector)) {
try {
// Get subscriber details
echo "8. Getting subscriber details...\n";
$subscriber = $client->getSubscriber($storeId, $offeringId, $customerSelector);
echo "Customer ID: " . $subscriber->getCustomer()->getId() . "\n";
echo "Active: " . ($subscriber->isActive() ? 'Yes' : 'No') . "\n";
echo "Phase: " . $subscriber->getPhase() . "\n";
echo "Created: " . date('Y-m-d H:i:s', $subscriber->getCreated()) . "\n";
echo "\n";
// Suspend subscriber
if (!empty($suspensionReason)) {
echo "9. Suspending subscriber...\n";
$client->suspendSubscriber($storeId, $offeringId, $customerSelector, $suspensionReason);
echo "Subscriber suspended successfully!\n";
// Check status after suspension
$suspendedSubscriber = $client->getSubscriber($storeId, $offeringId, $customerSelector);
echo "Status after suspension: " . ($suspendedSubscriber->isActive() ? 'Active' : 'Suspended') . "\n";
echo "Suspension reason: " . ($suspendedSubscriber->getSuspensionReason() ?? 'N/A') . "\n\n";
// Update subscriber dates
echo "9b. Updating subscriber dates...\n";
$updatedSubscriber = $client->updateSubscriberDates(
$storeId,
$offeringId,
$customerSelector,
null,
time() + (30 * 24 * 60 * 60) // 30 days from now
);
echo "Subscriber expiration updated\n";
echo "Scheduled plan: " . ($updatedSubscriber->getScheduledPlan() ? $updatedSubscriber->getScheduledPlan()->getName() : 'None') . "\n";
echo "Scheduled plan activates at: " . ($updatedSubscriber->getScheduledPlanActivatesAt() ? date('Y-m-d H:i:s', $updatedSubscriber->getScheduledPlanActivatesAt()) : 'N/A') . "\n\n";
// Unsuspend subscriber
echo "10. Unsuspending subscriber...\n";
$client->unsuspendSubscriber($storeId, $offeringId, $customerSelector);
echo "Subscriber unsuspended successfully!\n";
// Check status after unsuspending
$reactivatedSubscriber = $client->getSubscriber($storeId, $offeringId, $customerSelector);
echo "Status after unsuspending: " . ($reactivatedSubscriber->isActive() ? 'Active' : 'Suspended') . "\n";
echo "Suspension reason: " . ($reactivatedSubscriber->getSuspensionReason() ?? 'N/A') . "\n\n";
}
// Delete subscriber
echo "11. Deleting subscriber...\n";
$client->deleteSubscriber($storeId, $offeringId, $customerSelector);
echo "Subscriber deleted successfully!\n\n";
} catch (\Throwable $e) {
echo "Error in subscriber management: " . $e->getMessage() . "\n";
}
} else {
echo "8. Subscriber management examples skipped - please fill in storeId, offeringId, and customerSelector variables\n";
}
*/
echo "=== Examples completed successfully! ===\n";
} catch (\Throwable $e) {
echo "Error: " . $e->getMessage() . "\n";
echo "Stack trace:\n" . $e->getTraceAsString() . "\n";
}

View File

@ -49,7 +49,7 @@ class Users
}
}
public function deleteUser($userId)
public function deleteUser(string $userId)
{
try {
$client = new User($this->host, $this->apiKey);
@ -59,7 +59,7 @@ class Users
}
}
public function setUserLock($userId, $toggle)
public function setUserLock(string $userId, bool $toggle)
{
try {
$client = new User($this->host, $this->apiKey);

View File

@ -22,7 +22,7 @@ class AbstractClient
/** @var ClientInterface */
private $httpClient;
public function __construct(string $baseUrl, string $apiKey, ?ClientInterface $client = null)
public function __construct(string $baseUrl, string $apiKey, ClientInterface $client = null)
{
$this->baseUrl = rtrim($baseUrl, '/');
$this->apiKey = $apiKey;

View File

@ -4,9 +4,11 @@ declare(strict_types=1);
namespace BTCPayServer\Client;
use BTCPayServer\Result\Health as ResultHealth;
class Health extends AbstractClient
{
public function getHealthStatus(): bool
public function getHealthStatus(): ResultHealth
{
$url = $this->getApiUrl() . 'health';
$headers = $this->getRequestHeaders();
@ -15,7 +17,9 @@ class Health extends AbstractClient
$response = $this->getHttpClient()->request($method, $url, $headers);
if ($response->getStatus() === 200) {
return true;
return new ResultHealth(
json_decode($response->getBody(), true, 512, JSON_THROW_ON_ERROR)
);
} else {
throw $this->getExceptionByStatusCode($method, $url, $response);
}

View File

@ -7,7 +7,6 @@ namespace BTCPayServer\Client;
use BTCPayServer\Result\Invoice as ResultInvoice;
use BTCPayServer\Result\InvoiceList;
use BTCPayServer\Result\InvoicePaymentMethod;
use BTCPayServer\Result\PullPayment as ResultPullPayment;
use BTCPayServer\Util\PreciseNumber;
class Invoice extends AbstractClient
@ -116,71 +115,19 @@ class Invoice extends AbstractClient
}
}
public function getAllInvoices(
string $storeId,
?int $take = null,
?int $skip = null
): InvoiceList {
return $this->getAllInvoicesWithFilter($storeId, null, null, null, null, null, $take, $skip);
public function getAllInvoices(string $storeId): InvoiceList
{
return $this->_getAllInvoicesWithFilter($storeId, null);
}
public function getInvoicesByOrderIds(
string $storeId,
array $orderIds,
?int $take = null,
?int $skip = null
): InvoiceList {
return $this->getAllInvoicesWithFilter($storeId, $orderIds, null, null, null, null, $take, $skip);
public function getInvoicesByOrderIds(string $storeId, array $orderIds): InvoiceList
{
return $this->_getAllInvoicesWithFilter($storeId, $orderIds);
}
public function getInvoicesByText(
private function _getAllInvoicesWithFilter(
string $storeId,
string $text,
?int $take = null,
?int $skip = null
): InvoiceList {
return $this->getAllInvoicesWithFilter($storeId, null, $text, null, null, null, $take, $skip);
}
public function getInvoicesByStatus(
string $storeId,
array $status,
?int $take = null,
?int $skip = null
): InvoiceList {
return $this->getAllInvoicesWithFilter($storeId, null, null, $status, null, null, $take, $skip);
}
public function getInvoicesByStartDate(
string $storeId,
int $startDate,
?int $take = null,
?int $skip = null
): InvoiceList {
return $this->getAllInvoicesWithFilter($storeId, null, null, null, $startDate, null, $take, $skip);
}
public function getInvoicesByEndDate(
string $storeId,
int $endDate,
?int $take = null,
?int $skip = null
): InvoiceList {
return $this->getAllInvoicesWithFilter($storeId, null, null, null, null, $endDate, $take, $skip);
}
/**
* @see https://docs.btcpayserver.org/API/Greenfield/v1/#operation/Invoices_GetInvoices
*/
public function getAllInvoicesWithFilter(
string $storeId,
?array $filterByOrderIds = null,
?string $filterByText = null,
?array $filterByStatus = null,
?int $filterByStartDate = null,
?int $filterByEndDate = null,
?int $take = null,
?int $skip = null
array $filterByOrderIds = null
): InvoiceList {
$url = $this->getApiUrl() . 'stores/' . urlencode($storeId) . '/invoices?';
if ($filterByOrderIds !== null) {
@ -188,26 +135,6 @@ class Invoice extends AbstractClient
$url .= 'orderId=' . urlencode($filterByOrderId) . '&';
}
}
if ($filterByText !== null) {
$url .= 'textSearch=' . urlencode($filterByText) . '&';
}
if ($filterByStatus !== null) {
foreach ($filterByStatus as $filterByStatusItem) {
$url .= 'status=' . urlencode($filterByStatusItem) . '&';
}
}
if ($filterByStartDate !== null) {
$url .= 'startDate=' . $filterByStartDate . '&';
}
if ($filterByEndDate !== null) {
$url .= 'endDate=' . $filterByEndDate . '&';
}
if ($take !== null) {
$url .= 'take=' . $take . '&';
}
if ($skip !== null) {
$url .= 'skip=' . $skip . '&';
}
// Clean URL.
$url = rtrim($url, '&');
@ -232,8 +159,7 @@ class Invoice extends AbstractClient
public function getPaymentMethods(string $storeId, string $invoiceId): array
{
$method = 'GET';
$url = $this->getApiUrl() . 'stores/' . urlencode($storeId) . '/invoices/'
. urlencode($invoiceId) . '/payment-methods';
$url = $this->getApiUrl() . 'stores/' . urlencode($storeId) . '/invoices/' . urlencode($invoiceId) . '/payment-methods';
$headers = $this->getRequestHeaders();
$response = $this->getHttpClient()->request($method, $url, $headers);
@ -255,15 +181,11 @@ class Invoice extends AbstractClient
}
}
/**
* Mark an invoice status.
*
* @see https://docs.btcpayserver.org/API/Greenfield/v1/#operation/Invoices_MarkInvoiceStatus
* @throws \JsonException
*/
public function markInvoiceStatus(string $storeId, string $invoiceId, string $markAs): ResultInvoice
{
$url = $this->getApiUrl() . 'stores/' . urlencode($storeId) . '/invoices/' . urlencode($invoiceId) . '/status';
$url = $this->getApiUrl() . 'stores/' . urlencode(
$storeId
) . '/invoices/' . urlencode($invoiceId) . '/status';
$headers = $this->getRequestHeaders();
$method = 'POST';
@ -285,36 +207,18 @@ class Invoice extends AbstractClient
}
}
/**
* Refund an invoice.
*
* @see https://docs.btcpayserver.org/API/Greenfield/v1/#operation/Invoices_Refund
* @throws \JsonException
*/
public function refundInvoice(
string $storeId,
string $invoiceId,
?string $refundVariant = 'CurrentRate',
?string $paymentMethod = 'BTC',
?string $name = null,
?string $description = null,
?float $subtractPercentage = 0.0,
?PreciseNumber $customAmount = null,
?string $customCurrency = null
): ResultPullPayment {
$url = $this->getApiUrl() . 'stores/' . urlencode($storeId) . '/invoices/' . urlencode($invoiceId) . '/refund';
public function archiveInvoice(string $storeId, string $invoiceId): bool
{
$url = $this->getApiUrl() . 'stores/' . urlencode(
$storeId
) . '/invoices/' . urlencode($invoiceId);
$headers = $this->getRequestHeaders();
$method = 'POST';
$method = 'DELETE';
$body = json_encode(
[
'name' => $name,
'description' => $description,
'paymentMethod' => $paymentMethod,
'refundVariant' => $refundVariant,
'subtractPercentage' => $subtractPercentage,
'customAmount' => $customAmount?->__toString(),
'customCurrency' => $customCurrency
'storeId' => $storeId,
'invoiceId' => $invoiceId
],
JSON_THROW_ON_ERROR
);
@ -322,9 +226,61 @@ class Invoice extends AbstractClient
$response = $this->getHttpClient()->request($method, $url, $headers, $body);
if ($response->getStatus() === 200) {
return new ResultPullPayment(
json_decode($response->getBody(), true, 512, JSON_THROW_ON_ERROR)
);
return true;
} else {
throw $this->getExceptionByStatusCode($method, $url, $response);
}
}
public function unarchiveInvoice(string $storeId, string $invoiceId): bool
{
$url = $this->getApiUrl() . 'stores/' . urlencode(
$storeId
) . '/invoices/' . urlencode($invoiceId) . '/unarchive';
$headers = $this->getRequestHeaders();
$method = 'POST';
$body = json_encode(
[
'storeId' => $storeId,
'invoiceId' => $invoiceId
],
JSON_THROW_ON_ERROR
);
$response = $this->getHttpClient()->request($method, $url, $headers, $body);
if ($response->getStatus() === 200) {
return true;
} else {
throw $this->getExceptionByStatusCode($method, $url, $response);
}
}
public function activatePaymentMethod(
string $storeId,
string $invoiceId,
string $paymentMethod,
): bool {
$url = $this->getApiUrl() . 'stores/' . urlencode(
$storeId
) . '/invoices/' . urlencode($invoiceId) . '/payment-methods/' . urlencode($paymentMethod) . '/activate';
$headers = $this->getRequestHeaders();
$method = 'POST';
$body = json_encode(
[
'storeId' => $storeId,
'invoiceId' => $invoiceId,
'paymentMethod' => $paymentMethod
],
JSON_THROW_ON_ERROR
);
$response = $this->getHttpClient()->request($method, $url, $headers, $body);
if ($response->getStatus() === 200) {
return true;
} else {
throw $this->getExceptionByStatusCode($method, $url, $response);
}

View File

@ -26,7 +26,7 @@ class InvoiceCheckoutOptions
/** @var int */
protected $monitoringMinutes;
/** @var int */
/** @var float */
protected $paymentTolerance;
/** @var string */
@ -38,15 +38,19 @@ class InvoiceCheckoutOptions
/** @var string */
protected $defaultLanguage;
/** @var bool */
protected $requiresRefundEmail;
public static function create(
?string $speedPolicy,
?array $paymentMethods,
?int $expirationMinutes,
?int $monitoringMinutes,
?int $paymentTolerance,
?float $paymentTolerance,
?string $redirectURL,
?bool $redirectAutomatically,
?string $defaultLanguage
?string $defaultLanguage,
?bool $requiresRefundEmail = false,
) {
$options = new InvoiceCheckoutOptions();
$options->setSpeedPolicy($speedPolicy);
@ -56,7 +60,9 @@ class InvoiceCheckoutOptions
$options->paymentTolerance = $paymentTolerance;
$options->redirectURL = $redirectURL;
$options->redirectAutomatically = $redirectAutomatically;
$options->requiresRefundEmail = $requiresRefundEmail;
$options->defaultLanguage = $defaultLanguage;
$options->requiresRefundEmail = $requiresRefundEmail;
return $options;
}
@ -113,12 +119,12 @@ class InvoiceCheckoutOptions
return $this;
}
public function getPaymentTolerance(): ?int
public function getPaymentTolerance(): ?float
{
return $this->paymentTolerance;
}
public function setPaymentTolerance(?int $paymentTolerance): self
public function setPaymentTolerance(?float $paymentTolerance): self
{
$this->paymentTolerance = $paymentTolerance;
return $this;
@ -146,6 +152,17 @@ class InvoiceCheckoutOptions
return $this;
}
public function isRequiresRefundEmail(): ?bool
{
return $this->requiresRefundEmail;
}
public function setRequiresRefundEmail(?bool $requiresRefundEmail): self
{
$this->requiresRefundEmail = $requiresRefundEmail;
return $this;
}
public function getDefaultLanguage(): ?string
{
return $this->defaultLanguage;
@ -170,7 +187,7 @@ class InvoiceCheckoutOptions
$lastIndex = strrpos($k, $separator);
if ($lastIndex !== false) {
$k = substr($k, $lastIndex + 1);
$k = substr($k, $lastIndex +1);
}
$array[$k] = $v;
}

View File

@ -138,9 +138,16 @@ class LightningInternalNode extends AbstractClient
}
}
/**
* Amount wrapped in a string, represented in a millistatoshi string.
* (1000 millisatoshi = 1 satoshi.
*
* @param string $amount
*/
public function payLightningInvoice(
string $cryptoCode,
string $BOLT11,
?string $amount,
?string $maxFeePercent,
?string $maxFeeFlat
): LightningPayment {
@ -153,6 +160,7 @@ class LightningInternalNode extends AbstractClient
$body = json_encode(
[
'BOLT11' => $BOLT11,
'amount' => $amount,
'maxFeePercent' => $maxFeePercent,
'maxFeeFlat' => $maxFeeFlat,
],

View File

@ -129,7 +129,7 @@ class LightningStore extends AbstractClient
}
}
public function getInvoice(
public function getLightningInvoice(
string $cryptoCode,
string $storeId,
string $id
@ -152,21 +152,33 @@ class LightningStore extends AbstractClient
}
}
/**
* Amount wrapped in a string, represented in a millistatoshi string.
* (1000 millisatoshi = 1 satoshi.
*
* @param string $amount
*/
public function payLightningInvoice(
string $cryptoCode,
string $storeId,
string $BOLT11
string $BOLT11,
?string $amount = null,
?string $maxFeePercent = null,
?string $maxFeeFlat = null,
): LightningPayment {
$url = $this->getApiUrl() . 'stores/' .
urlencode($storeId) . '/lightning/' .
urlencode($cryptoCode) . '/info';
urlencode($cryptoCode) . '/invoices/pay';
$headers = $this->getRequestHeaders();
$method = 'POST';
$body = json_encode(
[
'BOLT11' => $BOLT11
'BOLT11' => $BOLT11,
'amount' => $amount,
'maxFeePercent' => $maxFeePercent,
'maxFeeFlat' => $maxFeeFlat,
],
JSON_THROW_ON_ERROR
);

View File

@ -4,14 +4,14 @@ declare(strict_types=1);
namespace BTCPayServer\Client;
use BTCPayServer\Result\InvoiceCheckoutHTML;
use BTCPayServer\Result\InvoiceCheckoutHtml;
use BTCPayServer\Result\LanguageCodeList;
use BTCPayServer\Result\PermissionMetadata;
use BTCPayServer\Result\PermissionMetadataList;
use BTCPayServer\Result\RateSourceList;
class Miscellaneous extends AbstractClient
{
public function getPermissionMetadata(): PermissionMetadata
public function getPermissionMetadata(): PermissionMetadataList
{
$url = $this->getBaseUrl() . '/misc/permissions';
$headers = $this->getRequestHeaders();
@ -20,7 +20,7 @@ class Miscellaneous extends AbstractClient
$response = $this->getHttpClient()->request($method, $url, $headers);
if ($response->getStatus() === 200) {
return new PermissionMetadata(
return new PermissionMetadataList(
json_decode($response->getBody(), true, 512, JSON_THROW_ON_ERROR)
);
} else {
@ -48,7 +48,7 @@ class Miscellaneous extends AbstractClient
public function getInvoiceCheckout(
string $invoiceId,
?string $lang
): InvoiceCheckoutHTML {
): InvoiceCheckoutHtml {
$url = $this->getBaseUrl() . '/i/' . urlencode($invoiceId);
//set language query parameter if passed
@ -62,9 +62,7 @@ class Miscellaneous extends AbstractClient
$response = $this->getHttpClient()->request($method, $url, $headers);
if ($response->getStatus() === 200) {
return new InvoiceCheckoutHTML(
json_decode($response->getBody(), true, 512, JSON_THROW_ON_ERROR)
);
return new InvoiceCheckoutHtml($response->getBody());
} else {
throw $this->getExceptionByStatusCode($method, $url, $response);
}

View File

@ -17,19 +17,13 @@ class PullPayment extends AbstractClient
bool $includeArchived
): PullPaymentList {
$url = $this->getApiUrl() . 'stores/' .
urlencode($storeId) . '/pull-payments';
urlencode($storeId) . '/pull-payments?includeArchived=' .
($includeArchived ? 'true' : 'false');
$headers = $this->getRequestHeaders();
$method = 'GET';
$body = json_encode(
[
'includeArchived' => $includeArchived,
],
JSON_THROW_ON_ERROR
);
$response = $this->getHttpClient()->request($method, $url, $headers, $body);
$response = $this->getHttpClient()->request($method, $url, $headers);
if ($response->getStatus() === 200) {
return new PullPaymentList(
@ -50,8 +44,7 @@ class PullPayment extends AbstractClient
?bool $autoApproveClaims = false,
?int $startsAt,
?int $expiresAt,
?array $paymentMethods = null,
?string $description = null
array $paymentMethods
): ResultPullPayment {
$url = $this->getApiUrl() . 'stores/' .
urlencode($storeId) . '/pull-payments';
@ -62,7 +55,6 @@ class PullPayment extends AbstractClient
$body = json_encode(
[
'name' => $name,
'description' => $description,
'amount' => $amount->__toString(),
'currency' => $currency,
'period' => $period,
@ -70,8 +62,7 @@ class PullPayment extends AbstractClient
'autoApproveClaims' => $autoApproveClaims,
'startsAt' => $startsAt,
'expiresAt' => $expiresAt,
'paymentMethods' => $paymentMethods,
'payoutMethods' => $paymentMethods
'paymentMethods' => $paymentMethods
],
JSON_THROW_ON_ERROR
);
@ -244,7 +235,6 @@ class PullPayment extends AbstractClient
'destination' => $destination,
'amount' => $amount->__toString(),
'paymentMethod' => $paymentMethod,
'payoutMethodId' => $paymentMethod, // BTCPay 2.0.0 compatibilty
],
JSON_THROW_ON_ERROR
);

View File

@ -5,6 +5,7 @@ declare(strict_types=1);
namespace BTCPayServer\Client;
use BTCPayServer\Result\Store as ResultStore;
use BTCPayServer\Result\StoreList;
class Store extends AbstractClient
{
@ -20,7 +21,7 @@ class Store extends AbstractClient
int $paymentTolerance = 0,
bool $anyoneCanCreateInvoice = false,
bool $requiresRefundEmail = false,
?string $checkoutType = 'V2',
?string $checkoutType = 'V1',
?array $receipt = null,
bool $lightningAmountInSatoshi = false,
bool $lightningPrivateRouteHints = false,
@ -35,15 +36,7 @@ class Store extends AbstractClient
string $networkFeeMode = 'MultiplePaymentsOnly',
bool $payJoinEnabled = false,
bool $lazyPaymentMethods = false,
string $defaultPaymentMethod = 'BTC',
?string $supportUrl = null,
bool $archived = false,
bool $autodetectLanguage = false,
bool $showPayInWalletButton = true,
bool $showStoreHeader = true,
bool $celebratePayment = true,
bool $playSoundOnPayment = false,
?array $paymentMethodCriteria = null
string $defaultPaymentMethod = 'BTC'
): ResultStore {
$url = $this->getApiUrl() . 'stores';
$headers = $this->getRequestHeaders();
@ -53,7 +46,6 @@ class Store extends AbstractClient
[
"name" => $name,
"website" => $website,
"supportUrl" => $supportUrl,
"defaultCurrency" => $defaultCurrency,
"invoiceExpiration" => $invoiceExpiration,
"displayExpirationTimer" => $displayExpirationTimer,
@ -61,7 +53,6 @@ class Store extends AbstractClient
"speedPolicy" => $speedPolicy,
"lightningDescriptionTemplate" => $lightningDescriptionTemplate,
"paymentTolerance" => $paymentTolerance,
"archived" => $archived,
"anyoneCanCreateInvoice" => $anyoneCanCreateInvoice,
"requiresRefundEmail" => $requiresRefundEmail,
"checkoutType" => $checkoutType,
@ -78,14 +69,8 @@ class Store extends AbstractClient
"htmlTitle" => $htmlTitle,
"networkFeeMode" => $networkFeeMode,
"payJoinEnabled" => $payJoinEnabled,
"autodetectLanguage" => $autodetectLanguage,
"showPayInWalletButton" => $showPayInWalletButton,
"showStoreHeader" => $showStoreHeader,
"celebratePayment" => $celebratePayment,
"playSoundOnPayment" => $playSoundOnPayment,
"lazyPaymentMethods" => $lazyPaymentMethods,
"defaultPaymentMethod" => $defaultPaymentMethod,
"paymentMethodCriteria" => $paymentMethodCriteria
"defaultPaymentMethod" => $defaultPaymentMethod
],
JSON_THROW_ON_ERROR
);
@ -114,118 +99,9 @@ class Store extends AbstractClient
}
/**
* Update store settings. Make sure to pass all the settings, even if you don't want to change them.
* @return \BTCPayServer\Result\StoreList
*/
public function updateStore(
string $storeId,
string $name,
?string $website = null,
string $defaultCurrency = 'USD',
int $invoiceExpiration = 900,
int $displayExpirationTimer = 300,
int $monitoringExpiration = 3600,
string $speedPolicy = 'MediumSpeed',
?string $lightningDescriptionTemplate = null,
int $paymentTolerance = 0,
bool $anyoneCanCreateInvoice = false,
bool $requiresRefundEmail = false,
?string $checkoutType = 'V2',
?array $receipt = null,
bool $lightningAmountInSatoshi = false,
bool $lightningPrivateRouteHints = false,
bool $onChainWithLnInvoiceFallback = false,
bool $redirectAutomatically = false,
bool $showRecommendedFee = true,
int $recommendedFeeBlockTarget = 1,
string $defaultLang = 'en',
?string $customLogo = null,
?string $customCSS = null,
?string $htmlTitle = null,
string $networkFeeMode = 'MultiplePaymentsOnly',
bool $payJoinEnabled = false,
bool $lazyPaymentMethods = false,
string $defaultPaymentMethod = 'BTC',
?string $supportUrl = null,
bool $archived = false,
bool $autodetectLanguage = false,
bool $showPayInWalletButton = true,
bool $showStoreHeader = true,
bool $celebratePayment = true,
bool $playSoundOnPayment = false,
?array $paymentMethodCriteria = null
): ResultStore {
$url = $this->getApiUrl() . 'stores/' . urlencode($storeId);
$headers = $this->getRequestHeaders();
$method = 'PUT';
$body = json_encode(
[
"name" => $name,
"website" => $website,
"supportUrl" => $supportUrl,
"defaultCurrency" => $defaultCurrency,
"invoiceExpiration" => $invoiceExpiration,
"displayExpirationTimer" => $displayExpirationTimer,
"monitoringExpiration" => $monitoringExpiration,
"speedPolicy" => $speedPolicy,
"lightningDescriptionTemplate" => $lightningDescriptionTemplate,
"paymentTolerance" => $paymentTolerance,
"archived" => $archived,
"anyoneCanCreateInvoice" => $anyoneCanCreateInvoice,
"requiresRefundEmail" => $requiresRefundEmail,
"checkoutType" => $checkoutType,
"receipt" => $receipt,
"lightningAmountInSatoshi" => $lightningAmountInSatoshi,
"lightningPrivateRouteHints" => $lightningPrivateRouteHints,
"onChainWithLnInvoiceFallback" => $onChainWithLnInvoiceFallback,
"redirectAutomatically" => $redirectAutomatically,
"showRecommendedFee" => $showRecommendedFee,
"recommendedFeeBlockTarget" => $recommendedFeeBlockTarget,
"defaultLang" => $defaultLang,
"customLogo" => $customLogo,
"customCSS" => $customCSS,
"htmlTitle" => $htmlTitle,
"networkFeeMode" => $networkFeeMode,
"payJoinEnabled" => $payJoinEnabled,
"autodetectLanguage" => $autodetectLanguage,
"showPayInWalletButton" => $showPayInWalletButton,
"showStoreHeader" => $showStoreHeader,
"celebratePayment" => $celebratePayment,
"playSoundOnPayment" => $playSoundOnPayment,
"lazyPaymentMethods" => $lazyPaymentMethods,
"defaultPaymentMethod" => $defaultPaymentMethod,
"paymentMethodCriteria" => $paymentMethodCriteria
],
JSON_THROW_ON_ERROR
);
$response = $this->getHttpClient()->request($method, $url, $headers, $body);
if ($response->getStatus() === 200) {
return new ResultStore(json_decode($response->getBody(), true, 512, JSON_THROW_ON_ERROR));
} else {
throw $this->getExceptionByStatusCode($method, $url, $response);
}
}
public function deleteStore(string $storeId): bool
{
$url = $this->getApiUrl() . 'stores/' . urlencode($storeId);
$headers = $this->getRequestHeaders();
$method = 'DELETE';
$response = $this->getHttpClient()->request($method, $url, $headers);
if ($response->getStatus() === 200) {
return true;
} else {
throw $this->getExceptionByStatusCode($method, $url, $response);
}
}
/**
* @return \BTCPayServer\Result\Store[]
*/
public function getStores(): array
public function getStores(): StoreList
{
$url = $this->getApiUrl() . 'stores';
$headers = $this->getRequestHeaders();
@ -233,13 +109,8 @@ class Store extends AbstractClient
$response = $this->getHttpClient()->request($method, $url, $headers);
if ($response->getStatus() === 200) {
$r = [];
$data = json_decode($response->getBody(), true);
foreach ($data as $item) {
$item = new ResultStore($item);
$r[] = $item;
}
return $r;
return new StoreList($data);
} else {
throw $this->getExceptionByStatusCode($method, $url, $response);
}

View File

@ -9,7 +9,7 @@ use BTCPayServer\Result\StoreOnChainWalletAddress;
use BTCPayServer\Result\StoreOnChainWalletFeeRate;
use BTCPayServer\Result\StoreOnChainWalletTransaction;
use BTCPayServer\Result\StoreOnChainWalletTransactionList;
use BTCPayServer\Result\StoreOnChainWalletUTXOList;
use BTCPayServer\Result\StoreOnChainWalletUtxoList;
class StoreOnChainWallet extends AbstractClient
{
@ -35,50 +35,6 @@ class StoreOnChainWallet extends AbstractClient
}
}
public function createStoreOnChainWallet(
string $storeId,
string $cryptoCode,
?string $existingMnemonic = null,
?string $passphrase = null,
int $accountNumber = 0,
bool $savePrivateKeys = false,
bool $importKeysToRPC = false,
string $wordList = 'English',
int $wordCount = 12,
string $scriptPubKeyType = 'Segwit'
): ResultStoreOnChainWallet {
$url = $this->getApiUrl() . 'stores/' .
urlencode($storeId) . '/payment-methods/onchain/' .
urlencode($cryptoCode) . '/generate';
$headers = $this->getRequestHeaders();
$method = 'POST';
$body = json_encode(
[
'existingMnemonic' => $existingMnemonic,
'passphrase' => $passphrase,
'accountNumber' => $accountNumber,
'savePrivateKeys' => $savePrivateKeys,
'importKeysToRPC' => $importKeysToRPC,
'wordList' => $wordList,
'wordCount' => $wordCount,
'scriptPubKeyType' => $scriptPubKeyType
],
JSON_THROW_ON_ERROR
);
$response = $this->getHttpClient()->request($method, $url, $headers, $body);
if ($response->getStatus() === 200) {
return new ResultStoreOnChainWallet(
json_decode($response->getBody(), true, 512, JSON_THROW_ON_ERROR)
);
} else {
throw $this->getExceptionByStatusCode($method, $url, $response);
}
}
public function getStoreOnChainWalletFeeRate(
string $storeId,
string $cryptoCode,
@ -285,10 +241,10 @@ class StoreOnChainWallet extends AbstractClient
}
}
public function getStoreOnChainWalletUTXOs(
public function getStoreOnChainWalletUtxos(
string $storeId,
string $cryptoCode
): StoreOnChainWalletUTXOList {
): StoreOnChainWalletUtxoList {
$url = $this->getApiUrl() . 'stores/' .
urlencode($storeId) . '/payment-methods' . '/OnChain' . '/' .
urlencode($cryptoCode) . '/wallet' . '/utxos';
@ -299,7 +255,7 @@ class StoreOnChainWallet extends AbstractClient
$response = $this->getHttpClient()->request($method, $url, $headers);
if ($response->getStatus() === 200) {
return new StoreOnChainWalletUTXOList(
return new StoreOnChainWalletUtxoList(
json_decode($response->getBody(), true, 512, JSON_THROW_ON_ERROR)
);
} else {

View File

@ -15,12 +15,9 @@ use BTCPayServer\Result\StorePaymentMethodCollection;
*/
class StorePaymentMethod extends AbstractClient
{
public function getPaymentMethods(string $storeId, bool $includeConfig = false): array
public function getPaymentMethods(string $storeId): array
{
$url = $this->getApiUrl() . 'stores/' . urlencode($storeId) . '/payment-methods';
if ($includeConfig) {
$url .= '?includeConfig=true';
}
$headers = $this->getRequestHeaders();
$method = 'GET';
$response = $this->getHttpClient()->request($method, $url, $headers);

View File

@ -10,8 +10,6 @@ use BTCPayServer\Result\StorePaymentMethodLightningNetwork as ResultStorePayment
* Handles a stores LightningNetwork payment methods.
*
* @see https://docs.btcpayserver.org/API/Greenfield/v1/#tag/Store-Payment-Methods-(Lightning-Network)
*
* @deprecated with BTCPay 2.0. Use \BTCPayServer\Client\StorePaymentMethod->getPaymentMethods() instead.
*/
class StorePaymentMethodLightningNetwork extends AbstractStorePaymentMethodClient
{

View File

@ -10,8 +10,6 @@ use BTCPayServer\Result\StorePaymentMethodOnChain as ResultStorePaymentMethodOnC
* Handles stores on chain payment methods.
*
* @see https://docs.btcpayserver.org/API/Greenfield/v1/#tag/Store-Payment-Methods-(On-Chain)
*
* @deprecated with BTCPay 2.0. Use \BTCPayServer\Client\StorePaymentMethod->getPaymentMethods() instead.
*/
class StorePaymentMethodOnChain extends AbstractStorePaymentMethodClient
{
@ -135,7 +133,7 @@ class StorePaymentMethodOnChain extends AbstractStorePaymentMethodClient
string $storeId,
string $cryptoCode,
string $derivationScheme,
?string $accountKeyPath = null
string $accountKeyPath = null
): array {
// todo: add offset + amount query parameters + check structure of derivationScheme etc.

View File

@ -97,7 +97,7 @@ class StoreRate extends AbstractClient
public function getRates(
string $storeId,
?array $currencyPairs = null
array $currencyPairs = null
): StoreRateList {
$url = $this->getApiUrl() . 'stores/' . urlencode($storeId) . '/rates?';
$headers = $this->getRequestHeaders();

View File

@ -1,493 +0,0 @@
<?php
declare(strict_types=1);
namespace BTCPayServer\Client;
use BTCPayServer\Result\Credit;
use BTCPayServer\Result\Offering;
use BTCPayServer\Result\OfferingList;
use BTCPayServer\Result\OfferingPlan;
use BTCPayServer\Result\PlanCheckout;
use BTCPayServer\Result\PortalSession;
use BTCPayServer\Result\Subscriber;
/**
* Handles subscriptions operations.
*
* @see https://docs.btcpayserver.org/API/Greenfield/v1/#tag/Subscriptions
*/
class Subscriptions extends AbstractClient
{
// Offering endpoints
public function getOffering(string $storeId, string $offeringId): Offering
{
$url = $this->getApiUrl() . 'stores/' . urlencode($storeId) . '/offerings/' . urlencode($offeringId);
$headers = $this->getRequestHeaders();
$method = 'GET';
$response = $this->getHttpClient()->request($method, $url, $headers);
if ($response->getStatus() === 200) {
return new Offering(json_decode($response->getBody(), true, 512, JSON_THROW_ON_ERROR));
} else {
throw $this->getExceptionByStatusCode($method, $url, $response);
}
}
public function getOfferings(string $storeId): OfferingList
{
$url = $this->getApiUrl() . 'stores/' . urlencode($storeId) . '/offerings';
$headers = $this->getRequestHeaders();
$method = 'GET';
$response = $this->getHttpClient()->request($method, $url, $headers);
if ($response->getStatus() === 200) {
return new OfferingList(json_decode($response->getBody(), true, 512, JSON_THROW_ON_ERROR));
} else {
throw $this->getExceptionByStatusCode($method, $url, $response);
}
}
public function createOffering(
string $storeId,
?string $appName = null,
?string $successRedirectUrl = null,
?array $metadata = null,
?array $features = null
): Offering {
$url = $this->getApiUrl() . 'stores/' . urlencode($storeId) . '/offerings';
$headers = $this->getRequestHeaders();
$method = 'POST';
$body = json_encode(
[
'appName' => $appName,
'successRedirectUrl' => $successRedirectUrl,
'metadata' => $metadata,
'features' => $features
],
JSON_THROW_ON_ERROR
);
$response = $this->getHttpClient()->request($method, $url, $headers, $body);
if ($response->getStatus() === 200 || $response->getStatus() === 201) {
return new Offering(json_decode($response->getBody(), true, 512, JSON_THROW_ON_ERROR));
} else {
throw $this->getExceptionByStatusCode($method, $url, $response);
}
}
public function updateOffering(
string $storeId,
string $offeringId,
?string $appName = null,
?string $successRedirectUrl = null,
?array $metadata = null,
?array $features = null
): Offering {
$url = $this->getApiUrl() . 'stores/' . urlencode($storeId) . '/offerings/' . urlencode($offeringId);
$headers = $this->getRequestHeaders();
$method = 'PUT';
$body = json_encode(
[
'appName' => $appName,
'successRedirectUrl' => $successRedirectUrl,
'metadata' => $metadata,
'features' => $features
],
JSON_THROW_ON_ERROR
);
$response = $this->getHttpClient()->request($method, $url, $headers, $body);
if ($response->getStatus() === 200) {
return new Offering(json_decode($response->getBody(), true, 512, JSON_THROW_ON_ERROR));
} else {
throw $this->getExceptionByStatusCode($method, $url, $response);
}
}
// Plan endpoints
public function createOfferingPlan(
string $storeId,
string $offeringId,
?string $description = null,
?string $currency = null,
?int $gracePeriodDays = null,
?string $name = null,
?bool $optimisticActivation = null,
?string $price = null,
?bool $renewable = null,
?int $trialDays = null,
?array $metadata = null,
?string $recurringType = null,
?array $features = null
): OfferingPlan {
$url = $this->getApiUrl() . 'stores/' . urlencode($storeId) . '/offerings/' . urlencode($offeringId) . '/plans';
$headers = $this->getRequestHeaders();
$method = 'POST';
$body = json_encode(
[
'description' => $description,
'currency' => $currency,
'gracePeriodDays' => $gracePeriodDays,
'name' => $name,
'optimisticActivation' => $optimisticActivation,
'price' => $price,
'renewable' => $renewable,
'trialDays' => $trialDays,
'metadata' => $metadata,
'recurringType' => $recurringType,
'features' => $features
],
JSON_THROW_ON_ERROR
);
$response = $this->getHttpClient()->request($method, $url, $headers, $body);
if ($response->getStatus() === 200 || $response->getStatus() === 201) {
return new OfferingPlan(json_decode($response->getBody(), true, 512, JSON_THROW_ON_ERROR));
} else {
throw $this->getExceptionByStatusCode($method, $url, $response);
}
}
public function getOfferingPlan(string $storeId, string $offeringId, string $planId): OfferingPlan
{
$url = $this->getApiUrl() . 'stores/' . urlencode($storeId) . '/offerings/' . urlencode($offeringId) . '/plans/' . urlencode($planId);
$headers = $this->getRequestHeaders();
$method = 'GET';
$response = $this->getHttpClient()->request($method, $url, $headers);
if ($response->getStatus() === 200) {
return new OfferingPlan(json_decode($response->getBody(), true, 512, JSON_THROW_ON_ERROR));
} else {
throw $this->getExceptionByStatusCode($method, $url, $response);
}
}
public function updateOfferingPlan(
string $storeId,
string $offeringId,
string $planId,
?string $description = null,
?string $currency = null,
?int $gracePeriodDays = null,
?string $name = null,
?bool $optimisticActivation = null,
?string $price = null,
?bool $renewable = null,
?int $trialDays = null,
?array $metadata = null,
?string $recurringType = null,
?array $features = null
): OfferingPlan {
$url = $this->getApiUrl() . 'stores/' . urlencode($storeId) . '/offerings/' . urlencode($offeringId) . '/plans/' . urlencode($planId);
$headers = $this->getRequestHeaders();
$method = 'PUT';
$body = json_encode(
[
'description' => $description,
'currency' => $currency,
'gracePeriodDays' => $gracePeriodDays,
'name' => $name,
'optimisticActivation' => $optimisticActivation,
'price' => $price,
'renewable' => $renewable,
'trialDays' => $trialDays,
'metadata' => $metadata,
'recurringType' => $recurringType,
'features' => $features
],
JSON_THROW_ON_ERROR
);
$response = $this->getHttpClient()->request($method, $url, $headers, $body);
if ($response->getStatus() === 200) {
return new OfferingPlan(json_decode($response->getBody(), true, 512, JSON_THROW_ON_ERROR));
} else {
throw $this->getExceptionByStatusCode($method, $url, $response);
}
}
// Subscriber endpoints
public function getSubscriber(string $storeId, string $offeringId, string $customerSelector): Subscriber
{
$url = $this->getApiUrl() . 'stores/' . urlencode($storeId) . '/offerings/' . urlencode($offeringId) . '/subscribers/' . urlencode($customerSelector);
$headers = $this->getRequestHeaders();
$method = 'GET';
$response = $this->getHttpClient()->request($method, $url, $headers);
if ($response->getStatus() === 200) {
return new Subscriber(json_decode($response->getBody(), true, 512, JSON_THROW_ON_ERROR));
} else {
throw $this->getExceptionByStatusCode($method, $url, $response);
}
}
public function deleteSubscriber(string $storeId, string $offeringId, string $customerSelector): void
{
$url = $this->getApiUrl() . 'stores/' . urlencode($storeId) . '/offerings/' . urlencode($offeringId) . '/subscribers/' . urlencode($customerSelector);
$headers = $this->getRequestHeaders();
$method = 'DELETE';
$response = $this->getHttpClient()->request($method, $url, $headers);
if ($response->getStatus() !== 204) {
throw $this->getExceptionByStatusCode($method, $url, $response);
}
}
public function updateSubscriberDates(
string $storeId,
string $offeringId,
string $customerSelector,
?int $startDate = null,
?int $expirationDate = null
): Subscriber {
$url = $this->getApiUrl() . 'stores/' . urlencode($storeId) . '/offerings/' . urlencode($offeringId) . '/subscribers/' . urlencode($customerSelector) . '/dates';
$headers = $this->getRequestHeaders();
$method = 'PUT';
$body = json_encode(
[
'startDate' => $startDate,
'expirationDate' => $expirationDate
],
JSON_THROW_ON_ERROR
);
$response = $this->getHttpClient()->request($method, $url, $headers, $body);
if ($response->getStatus() === 200) {
return new Subscriber(json_decode($response->getBody(), true, 512, JSON_THROW_ON_ERROR));
} else {
throw $this->getExceptionByStatusCode($method, $url, $response);
}
}
public function suspendSubscriber(string $storeId, string $offeringId, string $customerSelector, string $reason): Subscriber
{
$url = $this->getApiUrl() . 'stores/' . urlencode($storeId) . '/offerings/' . urlencode($offeringId) . '/subscribers/' . urlencode($customerSelector) . '/suspend';
$headers = $this->getRequestHeaders();
$method = 'POST';
$body = json_encode(
[
'reason' => $reason
],
JSON_THROW_ON_ERROR
);
$response = $this->getHttpClient()->request($method, $url, $headers, $body);
if ($response->getStatus() === 200) {
return new Subscriber(json_decode($response->getBody(), true, 512, JSON_THROW_ON_ERROR));
} else {
throw $this->getExceptionByStatusCode($method, $url, $response);
}
}
public function unsuspendSubscriber(string $storeId, string $offeringId, string $customerSelector): Subscriber
{
$url = $this->getApiUrl() . 'stores/' . urlencode($storeId) . '/offerings/' . urlencode($offeringId) . '/subscribers/' . urlencode($customerSelector) . '/unsuspend';
$headers = $this->getRequestHeaders();
$method = 'POST';
$response = $this->getHttpClient()->request($method, $url, $headers);
if ($response->getStatus() === 200) {
return new Subscriber(json_decode($response->getBody(), true, 512, JSON_THROW_ON_ERROR));
} else {
throw $this->getExceptionByStatusCode($method, $url, $response);
}
}
// Credit endpoints
public function getCredit(string $storeId, string $offeringId, string $customerSelector, string $currency): Credit
{
$url = $this->getApiUrl() . 'stores/' . urlencode($storeId) . '/offerings/' . urlencode($offeringId) . '/subscribers/' . urlencode($customerSelector) . '/credits/' . urlencode($currency);
$headers = $this->getRequestHeaders();
$method = 'GET';
$response = $this->getHttpClient()->request($method, $url, $headers);
if ($response->getStatus() === 200) {
return new Credit(json_decode($response->getBody(), true, 512, JSON_THROW_ON_ERROR));
} else {
throw $this->getExceptionByStatusCode($method, $url, $response);
}
}
public function updateCredit(
string $storeId,
string $offeringId,
string $customerSelector,
string $currency,
?string $credit = null,
?string $charge = null,
?string $description = null,
?bool $allowOverdraft = null
): Credit {
$url = $this->getApiUrl() . 'stores/' . urlencode($storeId) . '/offerings/' . urlencode($offeringId) . '/subscribers/' . urlencode($customerSelector) . '/credits/' . urlencode($currency);
$headers = $this->getRequestHeaders();
$method = 'POST';
$body = json_encode(
[
'credit' => $credit,
'charge' => $charge,
'description' => $description,
'allowOverdraft' => $allowOverdraft
],
JSON_THROW_ON_ERROR
);
$response = $this->getHttpClient()->request($method, $url, $headers, $body);
if ($response->getStatus() === 200) {
return new Credit(json_decode($response->getBody(), true, 512, JSON_THROW_ON_ERROR));
} else {
throw $this->getExceptionByStatusCode($method, $url, $response);
}
}
// Plan checkout endpoints
public function getPlanCheckout(string $checkoutId): PlanCheckout
{
$url = $this->getApiUrl() . 'plan-checkout/' . urlencode($checkoutId);
$headers = $this->getRequestHeaders();
$method = 'GET';
$response = $this->getHttpClient()->request($method, $url, $headers);
if ($response->getStatus() === 200) {
return new PlanCheckout(json_decode($response->getBody(), true, 512, JSON_THROW_ON_ERROR));
} else {
throw $this->getExceptionByStatusCode($method, $url, $response);
}
}
public function proceedPlanCheckout(string $checkoutId, ?string $email = null): PlanCheckout
{
$url = $this->getApiUrl() . 'plan-checkout/' . urlencode($checkoutId);
$headers = $this->getRequestHeaders();
$method = 'POST';
$params = [];
if ($email !== null) {
$params['email'] = $email;
}
if (!empty($params)) {
$url .= '?' . http_build_query($params);
}
$response = $this->getHttpClient()->request($method, $url, $headers);
if ($response->getStatus() === 200) {
return new PlanCheckout(json_decode($response->getBody(), true, 512, JSON_THROW_ON_ERROR));
} else {
throw $this->getExceptionByStatusCode($method, $url, $response);
}
}
public function createPlanCheckout(
string $storeId,
string $offeringId,
string $planId,
?string $customerSelector = null,
?int $durationMinutes = null,
?string $onPayBehavior = null,
?array $newSubscriberMetadata = null,
?array $invoiceMetadata = null,
?array $metadata = null,
?bool $isTrial = null,
?string $creditPurchase = null,
?string $successRedirectLink = null,
?string $newSubscriberEmail = null
): PlanCheckout {
$url = $this->getApiUrl() . 'plan-checkout';
$headers = $this->getRequestHeaders();
$method = 'POST';
$body = json_encode(
[
'storeId' => $storeId,
'offeringId' => $offeringId,
'planId' => $planId,
'customerSelector' => $customerSelector,
'durationMinutes' => $durationMinutes,
'onPayBehavior' => $onPayBehavior,
'newSubscriberMetadata' => $newSubscriberMetadata,
'invoiceMetadata' => $invoiceMetadata,
'metadata' => $metadata,
'isTrial' => $isTrial,
'creditPurchase' => $creditPurchase,
'successRedirectLink' => $successRedirectLink,
'newSubscriberEmail' => $newSubscriberEmail
],
JSON_THROW_ON_ERROR
);
$response = $this->getHttpClient()->request($method, $url, $headers, $body);
if ($response->getStatus() === 200) {
return new PlanCheckout(json_decode($response->getBody(), true, 512, JSON_THROW_ON_ERROR));
} else {
throw $this->getExceptionByStatusCode($method, $url, $response);
}
}
// Portal session endpoints
public function createPortalSession(
string $storeId,
string $offeringId,
string $customerSelector,
?int $durationMinutes = null
): PortalSession {
$url = $this->getApiUrl() . 'subscriber-portal';
$headers = $this->getRequestHeaders();
$method = 'POST';
$body = json_encode(
[
'storeId' => $storeId,
'offeringId' => $offeringId,
'customerSelector' => $customerSelector,
'durationMinutes' => $durationMinutes
],
JSON_THROW_ON_ERROR
);
$response = $this->getHttpClient()->request($method, $url, $headers, $body);
if ($response->getStatus() === 200) {
return new PortalSession(json_decode($response->getBody(), true, 512, JSON_THROW_ON_ERROR));
} else {
throw $this->getExceptionByStatusCode($method, $url, $response);
}
}
public function getPortalSession(string $portalSessionId): PortalSession
{
$url = $this->getApiUrl() . 'subscriber-portal/' . urlencode($portalSessionId);
$headers = $this->getRequestHeaders();
$method = 'GET';
$response = $this->getHttpClient()->request($method, $url, $headers);
if ($response->getStatus() === 200) {
return new PortalSession(json_decode($response->getBody(), true, 512, JSON_THROW_ON_ERROR));
} else {
throw $this->getExceptionByStatusCode($method, $url, $response);
}
}
}

View File

@ -59,7 +59,7 @@ class User extends AbstractClient
$response = $this->getHttpClient()->request($method, $url, $headers, $body);
if ($response->getStatus() === 201) {
if ($response->getStatus() === 200) {
return new ResultUser(
json_decode($response->getBody(), true, 512, JSON_THROW_ON_ERROR)
);

View File

@ -238,7 +238,7 @@ class Webhook extends AbstractClient
if ($requestBody && $btcpaySigHeader) {
$expectedHeader = 'sha256=' . hash_hmac('sha256', $requestBody, $secret);
if (hash_equals($expectedHeader, $btcpaySigHeader)) {
if ($expectedHeader === $btcpaySigHeader) {
return true;
}
}

View File

@ -6,7 +6,7 @@ namespace BTCPayServer\Exception;
class BTCPayException extends \RuntimeException
{
public function __construct(string $message, int $code, ?\Throwable $previous = null)
public function __construct(string $message, int $code, \Throwable $previous = null)
{
parent::__construct($message, $code, $previous);
}

View File

@ -6,7 +6,7 @@ namespace BTCPayServer\Result;
abstract class AbstractStorePaymentMethodResult extends AbstractResult
{
public function __construct(array $data, ?string $paymentMethod = null)
public function __construct(array $data, string $paymentMethod = null)
{
// Temporary workaround until the api provides paymentMethod.
if (!isset($data['paymentMethod'])) {

View File

@ -6,18 +6,4 @@ namespace BTCPayServer\Result;
class ApiKey extends AbstractResult
{
public function getApiKey(): string
{
return $this->getData()['apiKey'];
}
public function getLabel(): string
{
return $this->getData()['label'];
}
public function getPermissions(): array
{
return $this->getData()['permissions'];
}
}

View File

@ -1,18 +0,0 @@
<?php
declare(strict_types=1);
namespace BTCPayServer\Result;
class Credit extends AbstractResult
{
public function getCurrency(): string
{
return $this->getData()['currency'];
}
public function getValue(): string
{
return $this->getData()['value'];
}
}

View File

@ -1,33 +0,0 @@
<?php
declare(strict_types=1);
namespace BTCPayServer\Result;
class Customer extends AbstractResult
{
public function getStoreId(): string
{
return $this->getData()['storeId'];
}
public function getId(): string
{
return $this->getData()['id'];
}
public function getExternalId(): ?string
{
return $this->getData()['externalId'] ?? null;
}
public function getIdentities(): ?array
{
return $this->getData()['identities'] ?? null;
}
public function getMetadata(): ?array
{
return $this->getData()['metadata'] ?? null;
}
}

View File

@ -1,18 +0,0 @@
<?php
declare(strict_types=1);
namespace BTCPayServer\Result;
class Feature extends AbstractResult
{
public function getId(): string
{
return $this->getData()['id'];
}
public function getDescription(): string
{
return $this->getData()['description'];
}
}

14
src/Result/Health.php Normal file
View File

@ -0,0 +1,14 @@
<?php
declare(strict_types=1);
namespace BTCPayServer\Result;
class Health extends AbstractResult
{
public function isSyncronized(): bool
{
$data = $this->getData();
return $data['synchronized'];
}
}

View File

@ -4,6 +4,7 @@ declare(strict_types=1);
namespace BTCPayServer\Result;
use BTCPayServer\Client\InvoiceCheckoutOptions;
use BTCPayServer\Util\PreciseNumber;
class Invoice extends AbstractResult
@ -26,6 +27,11 @@ class Invoice extends AbstractResult
public const ADDITIONAL_STATUS_PAID_LATE = 'PaidLate';
public function getMetaData(): array
{
return $this->getData()['metadata'];
}
public function getId(): string
{
return $this->getData()['id'];
@ -36,6 +42,11 @@ class Invoice extends AbstractResult
return PreciseNumber::parseString($this->getData()['amount']);
}
public function getStoreId(): string
{
return $this->getData()['storeId'];
}
public function getCurrency(): string
{
return $this->getData()['currency'];
@ -61,9 +72,9 @@ class Invoice extends AbstractResult
return $this->getData()['expirationTime'];
}
public function getMonitoringTime(): int
public function getMonitoringExpiration(): int
{
return $this->getData()['monitoringTime'];
return $this->getData()['monitoringExpiration'];
}
public function isArchived(): bool
@ -71,6 +82,28 @@ class Invoice extends AbstractResult
return $this->getData()['archived'];
}
public function getCheckoutOptions(): InvoiceCheckoutOptions
{
$options = new InvoiceCheckoutOptions();
$options->setSpeedPolicy($this->getData()['checkout']['speedPolicy']);
$options->setPaymentMethods($this->getData()['checkout']['paymentMethods']);
$options->setExpirationMinutes($this->getData()['checkout']['expirationMinutes']);
$options->setMonitoringMinutes($this->getData()['checkout']['monitoringMinutes']);
$options->setPaymentTolerance($this->getData()['checkout']['paymentTolerance']);
$options->setRedirectURL($this->getData()['checkout']['redirectURL']);
$options->setRedirectAutomatically($this->getData()['checkout']['redirectAutomatically']);
$options->setRequiresRefundEmail($this->getData()['checkout']['requiresRefundEmail']);
$options->setDefaultLanguage($this->getData()['checkout']['defaultLanguage']);
return $options;
}
public function isPaid(): bool
{
$data = $this->getData();
return $data['status'] === self::STATUS_SETTLED || $data['additionalStatus'] === self::ADDITIONAL_STATUS_PAID_PARTIAL;
}
public function isNew(): bool
{
$data = $this->getData();
@ -88,6 +121,11 @@ class Invoice extends AbstractResult
return $this->getData()['status'];
}
public function getAdditionalStatus(): string
{
return $this->getData()['additionalStatus'];
}
public function isExpired(): bool
{
$data = $this->getData();

View File

@ -1,9 +0,0 @@
<?php
declare(strict_types=1);
namespace BTCPayServer\Result;
class InvoiceCheckoutHTML extends AbstractResult
{
}

View File

@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
namespace BTCPayServer\Result;
class InvoiceCheckoutHtml
{
/**
* @var string
*/
private $html;
public function __construct(string $html)
{
$this->html = $html;
}
public function getHtml(): string
{
return $this->html;
}
public function __toString()
{
return $this->html;
}
}

View File

@ -59,37 +59,25 @@ class InvoicePaymentMethod extends AbstractResult
public function getNetworkFee(): string
{
$data = $this->getData();
// BTCPay 2.0.0 compatibility: networkFee was renamed to paymentMethodFee.
return $data['networkFee'] ?? $data['paymentMethodFee'];
return $data['networkFee'];
}
public function getPaymentMethod(): string
{
$data = $this->getData();
// BTCPay 2.0.0 compatibility: paymentMethod was renamed to paymentMethodId.
return $data['paymentMethod'] ?? $data['paymentMethodId'];
return $data['paymentMethod'];
}
public function getCryptoCode(): string
{
$data = $this->getData();
// For future compatibility check if cryptoCode exists.
if (isset($data['cryptoCode'])) {
return $data['cryptoCode'];
} else {
// Extract cryptoCode from paymentMethod string.
$parts = explode('-', $this->getPaymentMethod());
$parts = explode('-', $data['paymentMethod']);
return $parts[0];
}
}
/**
* New field as of BTCPay 2.0.0.
*/
public function getCurrency(): ?string
{
$data = $this->getData();
return $data['currency'] ?? null;
}
}

View File

@ -7,13 +7,13 @@ namespace BTCPayServer\Result;
class LanguageCodeList extends AbstractListResult
{
/**
* @return \BTCPayServer\Result\LanguageCode[]
* @return LanguageCode[]
*/
public function all(): array
{
$languageCodes = [];
foreach ($this->getData() as $languageCode) {
$languageCodes[] = new \BTCPayServer\Result\LanguageCode($languageCode);
$languageCodes[] = new LanguageCode($languageCode);
}
return $languageCodes;
}

View File

@ -1,66 +0,0 @@
<?php
declare(strict_types=1);
namespace BTCPayServer\Result;
class Offering extends AbstractResult
{
public function getId(): string
{
return $this->getData()['id'];
}
public function getStoreId(): string
{
return $this->getData()['storeId'];
}
public function getAppId(): ?string
{
return $this->getData()['appId'] ?? null;
}
public function getAppName(): ?string
{
return $this->getData()['appName'] ?? null;
}
public function getSuccessRedirectUrl(): ?string
{
return $this->getData()['successRedirectUrl'] ?? null;
}
public function getMetadata(): ?array
{
return $this->getData()['metadata'] ?? null;
}
/**
* @return OfferingPlan[]
*/
public function getPlans(): array
{
$plans = [];
if (isset($this->getData()['plans']) && is_array($this->getData()['plans'])) {
foreach ($this->getData()['plans'] as $plan) {
$plans[] = new OfferingPlan($plan);
}
}
return $plans;
}
/**
* @return Feature[]
*/
public function getFeatures(): array
{
$features = [];
if (isset($this->getData()['features']) && is_array($this->getData()['features'])) {
foreach ($this->getData()['features'] as $feature) {
$features[] = new Feature($feature);
}
}
return $features;
}
}

View File

@ -1,20 +0,0 @@
<?php
declare(strict_types=1);
namespace BTCPayServer\Result;
class OfferingList extends AbstractListResult
{
/**
* @return Offering[]
*/
public function all(): array
{
$result = [];
foreach ($this->getData() as $item) {
$result[] = new Offering($item);
}
return $result;
}
}

View File

@ -1,81 +0,0 @@
<?php
declare(strict_types=1);
namespace BTCPayServer\Result;
class OfferingPlan extends AbstractResult
{
public function getId(): string
{
return $this->getData()['id'];
}
public function getName(): string
{
return $this->getData()['name'];
}
public function getStatus(): string
{
return $this->getData()['status'];
}
public function getPrice(): string
{
return $this->getData()['price'];
}
public function getCurrency(): string
{
return $this->getData()['currency'];
}
public function getRecurringType(): string
{
return $this->getData()['recurringType'];
}
public function getGracePeriodDays(): int
{
return $this->getData()['gracePeriodDays'];
}
public function getTrialDays(): int
{
return $this->getData()['trialDays'];
}
public function getDescription(): string
{
return $this->getData()['description'];
}
public function getMemberCount(): int
{
return $this->getData()['memberCount'];
}
public function isOptimisticActivation(): bool
{
return $this->getData()['optimisticActivation'];
}
public function isRenewable(): bool
{
return $this->getData()['renewable'];
}
/**
* @return string[]
*/
public function getFeatures(): array
{
return $this->getData()['features'] ?? [];
}
public function getMetadata(): ?array
{
return $this->getData()['metadata'] ?? null;
}
}

View File

@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace BTCPayServer\Result;
class PermissionMetadataList extends AbstractListResult
{
/**
* @return PermissionMetadata[]
*/
public function all(): array
{
$permissionMetadataList = [];
foreach ($this->getData() as $permissionMetadata) {
$permissionMetadataList[] = new PermissionMetadata($permissionMetadata);
}
return $permissionMetadataList;
}
}

View File

@ -1,113 +0,0 @@
<?php
declare(strict_types=1);
namespace BTCPayServer\Result;
class PlanCheckout extends AbstractResult
{
public function getId(): string
{
return $this->getData()['id'];
}
public function getSubscriber(): ?Subscriber
{
return isset($this->getData()['subscriber']) ? new Subscriber($this->getData()['subscriber']) : null;
}
public function getPlan(): OfferingPlan
{
return new OfferingPlan($this->getData()['plan']);
}
public function getBaseUrl(): string
{
return $this->getData()['baseUrl'];
}
public function getInvoiceId(): ?string
{
return $this->getData()['invoiceId'] ?? null;
}
public function getSuccessRedirectUrl(): ?string
{
return $this->getData()['successRedirectUrl'] ?? null;
}
public function getExpiration(): int
{
return $this->getData()['expiration'];
}
public function getRedirectUrl(): string
{
return $this->getData()['redirectUrl'];
}
public function getInvoiceMetadata(): ?array
{
return $this->getData()['invoiceMetadata'] ?? null;
}
public function getMetadata(): ?array
{
return $this->getData()['metadata'] ?? null;
}
public function isNewSubscriber(): bool
{
return $this->getData()['newSubscriber'];
}
public function isTrial(): bool
{
return $this->getData()['isTrial'];
}
public function getCreated(): int
{
return $this->getData()['created'];
}
public function isPlanStarted(): bool
{
return $this->getData()['planStarted'];
}
public function getNewSubscriberMetadata(): ?array
{
return $this->getData()['newSubscriberMetadata'] ?? null;
}
public function getRefundAmount(): ?string
{
return $this->getData()['refundAmount'] ?? null;
}
public function getCreditedByInvoice(): ?string
{
return $this->getData()['creditedByInvoice'] ?? null;
}
public function getOnPayBehavior(): ?string
{
return $this->getData()['onPayBehavior'] ?? null;
}
public function isExpired(): bool
{
return $this->getData()['isExpired'];
}
public function getUrl(): string
{
return $this->getData()['url'];
}
public function getCreditPurchase(): ?string
{
return $this->getData()['creditPurchase'] ?? null;
}
}

View File

@ -1,38 +0,0 @@
<?php
declare(strict_types=1);
namespace BTCPayServer\Result;
class PortalSession extends AbstractResult
{
public function getId(): string
{
return $this->getData()['id'];
}
public function getBaseUrl(): string
{
return $this->getData()['baseUrl'];
}
public function getSubscriber(): Subscriber
{
return new Subscriber($this->getData()['subscriber']);
}
public function getExpiration(): ?int
{
return $this->getData()['expiration'] ?? null;
}
public function isExpired(): bool
{
return $this->getData()['isExpired'];
}
public function getUrl(): string
{
return $this->getData()['url'];
}
}

View File

@ -20,12 +20,6 @@ class PullPayment extends AbstractResult
return $data['name'];
}
public function getDescription(): string
{
$data = $this->getData();
return $data['description'];
}
public function getCurrency(): string
{
$data = $this->getData();

View File

@ -10,61 +10,53 @@ class PullPaymentPayout extends AbstractResult
{
public function getId(): string
{
$data = $this->getData();
return $data['id'];
return $this->getData()['id'];
}
public function getRevision(): int
{
$data = $this->getData();
return $data['revision'];
return $this->getData()['revision'];
}
public function getPullPaymentId(): string
{
$data = $this->getData();
return $data['pullPaymentId'];
return $this->getData()['pullPaymentId'];
}
public function getDate(): string
public function getDate(): int
{
$data = $this->getData();
return $data['date'];
return $this->getData()['date'];
}
public function getDestination(): string
{
$data = $this->getData();
return $data['destination'];
return $this->getData()['destination'];
}
public function getAmount(): PreciseNumber
{
$data = $this->getData();
return PreciseNumber::parseString($data['amount']);
return PreciseNumber::parseString($this->getData()['amount']);
}
public function getPaymentMethod(): string
{
$data = $this->getData();
return $data['paymentMethod'];
return $this->getData()['paymentMethod'];
}
public function getCryptoCode(): string
{
$data = $this->getData();
return $data['cryptoCode'];
return $this->getData()['cryptoCode'];
}
public function getPaymentMethodAmount(): PreciseNumber
public function getPaymentMethodAmount(): ?PreciseNumber
{
$data = $this->getData();
return PreciseNumber::parseString($data['paymentMethodAmount']);
return (isset($this->getData()['paymentMethodAmount']))
? PreciseNumber::parseString($this->getData()['paymentMethodAmount'])
: null;
}
public function getState(): string
{
$data = $this->getData();
return $data['state'];
return $this->getData()['state'];
}
}

View File

@ -32,5 +32,8 @@ class ServerInfo extends AbstractResult
return $this->getData()['supportedPaymentMethods'];
}
// TODO add "syncStatus" structure
public function getSyncStatus(): ServerSyncStatusList
{
return new ServerSyncStatusList($this->getData()['syncStatus']);
}
}

View File

@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
namespace BTCPayServer\Result;
class ServerSyncStatus extends AbstractResult
{
public function getChainHeight(): int
{
return $this->getData()['chainHeight'];
}
public function getSyncHeight(): int
{
return $this->getData()['syncHeight'];
}
public function getNodeInformation(): ServerSyncStatusNodeInformation
{
return new ServerSyncStatusNodeInformation($this->getData()['nodeInformation']);
}
public function getCryptoCode(): string
{
return $this->getData()['cryptoCode'];
}
public function isAvailable(): bool
{
return $this->getData()['available'];
}
}

View File

@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace BTCPayServer\Result;
class ServerSyncStatusList extends AbstractResult
{
/**
* @return \BTCPayServer\Result\ServerSyncStatus[]
*/
public function all(): array
{
$serverSyncStatuses = [];
foreach ($this->getData() as $serverSyncStatus) {
$serverSyncStatuses[] = new \BTCPayServer\Result\ServerSyncStatus($serverSyncStatus);
}
return $serverSyncStatuses;
}
}

View File

@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace BTCPayServer\Result;
use BTCPayServer\Util\PreciseNumber;
class ServerSyncStatusNodeInformation extends AbstractResult
{
public function getHeaders(): int
{
return $this->getData()['headers'];
}
public function getBlocks(): int
{
return $this->getData()['blocks'];
}
public function getVerificationProgress(): PreciseNumber
{
return PreciseNumber::parseFloat($this->getData()['verificationProgress']);
}
}

View File

@ -4,6 +4,8 @@ declare(strict_types=1);
namespace BTCPayServer\Result;
use BTCPayServer\Util\PreciseNumber;
class Store extends AbstractResult
{
public function getName(): string
@ -12,13 +14,13 @@ class Store extends AbstractResult
return $data['name'];
}
public function getWebsite(): string
public function getWebsite(): ?string
{
$data = $this->getData();
return $data['website'];
}
public function getDefaultCurrency(): string
public function getDefaultCurrency(): ?string
{
$data = $this->getData();
return $data['defaultCurrency'];
@ -48,10 +50,10 @@ class Store extends AbstractResult
return $data['lightningDescriptionTemplate'];
}
public function getPaymentTolerance(): int
public function getPaymentTolerance(): PreciseNumber
{
$data = $this->getData();
return $data['paymentTolerance'];
return PreciseNumber::parseFloat($data['paymentTolerance']);
}
public function anyoneCanCreateInvoice(): bool
@ -102,25 +104,25 @@ class Store extends AbstractResult
return $data['recommendedFeeBlockTarget'];
}
public function getDefaultLang(): string
public function getDefaultLang(): ?string
{
$data = $this->getData();
return $data['defaultLang'];
}
public function getCustomLogo(): string
public function getCustomLogo(): ?string
{
$data = $this->getData();
return $data['customLogo'];
}
public function getCustomCSS(): string
public function getCustomCSS(): ?string
{
$data = $this->getData();
return $data['customCSS'];
}
public function getHtmlTitle(): string
public function getHtmlTitle(): ?string
{
$data = $this->getData();
return $data['htmlTitle'];
@ -144,7 +146,7 @@ class Store extends AbstractResult
return $data['lazyPaymentMethods'];
}
public function getDefaultPaymentMethod(): string
public function getDefaultPaymentMethod(): ?string
{
$data = $this->getData();
return $data['defaultPaymentMethod'];

View File

@ -6,39 +6,45 @@ namespace BTCPayServer\Result;
class StoreEmailSettings extends AbstractResult
{
public function getServer(): string
public function getServer(): ?string
{
$data = $this->getData();
return $data['server'];
}
public function getPort(): string
public function getPort(): ?int
{
$data = $this->getData();
return $data['port'];
}
public function getUsername(): string
public function getUsername(): ?string
{
$data = $this->getData();
return $data['login'];
}
public function getPassword(): string
public function getPassword(): ?string
{
$data = $this->getData();
return $data['password'];
}
public function getFromEmail(): string
public function getFromEmail(): ?string
{
$data = $this->getData();
return $data['from'];
}
public function getFromName(): string
public function getFromName(): ?string
{
$data = $this->getData();
return $data['fromDisplay'];
}
public function getDisableCertificateCheck(): ?bool
{
$data = $this->getData();
return $data['disableCertificateCheck'];
}
}

20
src/Result/StoreList.php Normal file
View File

@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace BTCPayServer\Result;
class StoreList extends AbstractListResult
{
/**
* @return Store[]
*/
public function all(): array
{
$stores = [];
foreach ($this->getData() as $store) {
$stores[] = new Store($store);
}
return $stores;
}
}

View File

@ -25,4 +25,10 @@ class StoreOnChainWallet extends AbstractResult
$data = $this->getData();
return PreciseNumber::parseString($data['confirmedBalance']);
}
public function getLabel(): string
{
$data = $this->getData();
return $data['label'];
}
}

View File

@ -4,11 +4,13 @@ declare(strict_types=1);
namespace BTCPayServer\Result;
use BTCPayServer\Util\PreciseNumber;
class StoreOnChainWalletFeeRate extends AbstractResult
{
public function getFeeRate(): float
public function getFeeRate(): PreciseNumber
{
$data = $this->getData();
return $data['feeRate'];
return PreciseNumber::parseFloat($data['feeRate']);
}
}

View File

@ -4,50 +4,61 @@ declare(strict_types=1);
namespace BTCPayServer\Result;
use BTCPayServer\Util\PreciseNumber;
class StoreOnChainWalletTransaction extends AbstractResult
{
public function getDestinations(): StoreOnChainWalletTransactionDestinationList
public function getTransactionHash(): string
{
$data = $this->getData();
return new StoreOnChainWalletTransactionDestinationList($data['destinations']);
return $data['transactionHash'];
}
public function getFeeRate(): StoreOnChainWalletFeeRate
public function getComment(): string
{
$data = $this->getData();
return new StoreOnChainWalletFeeRate($data['feeRate']);
return $data['comment'];
}
public function proceedWithPayjoin(): bool
public function getAmount(): PreciseNumber
{
$data = $this->getData();
return $data['proceedWithPayjoin'];
return new PreciseNumber($data['amount']);
}
public function proceedWithBroadcast(): bool
public function getLabels(): array
{
$data = $this->getData();
return $data['proceedWithBroadcast'];
return $data['labels'];
}
public function noChange(): bool
public function getBlockHash(): ?string
{
$data = $this->getData();
return $data['noChange'];
return $data['blockHash'];
}
public function rbf(): bool
public function getBlockHeight(): ?int
{
$data = $this->getData();
return $data['rbf'];
return $data['blockHeight'];
}
/**
* @return array strings
*/
public function selectedInputs(): array
public function getConfirmations(): int
{
$data = $this->getData();
return $data['selectedInputs'];
return $data['confirmations'];
}
public function getTimestamp(): int
{
$data = $this->getData();
return $data['timestamp'];
}
public function getStatus(): string
{
$data = $this->getData();
return $data['status'];
}
}

View File

@ -1,20 +0,0 @@
<?php
declare(strict_types=1);
namespace BTCPayServer\Result;
class StoreOnChainWalletUTXOList extends AbstractListResult
{
/**
* @return \BTCPayServer\Result\StoreOnChainWalletUTXO[]
*/
public function all(): array
{
$storeWalletUTXOs = [];
foreach ($this->getData() as $storeWalletUTXO) {
$storeWalletUTXOs[] = new \BTCPayServer\Result\StoreOnChainWalletUTXO($storeWalletUTXO);
}
return $storeWalletUTXOs;
}
}

View File

@ -6,7 +6,7 @@ namespace BTCPayServer\Result;
use BTCPayServer\Util\PreciseNumber;
class StoreOnChainWalletUTXO extends AbstractResult
class StoreOnChainWalletUtxo extends AbstractResult
{
public function getComment(): string
{

View File

@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace BTCPayServer\Result;
class StoreOnChainWalletUtxoList extends AbstractListResult
{
/**
* @return \BTCPayServer\Result\StoreOnChainWalletUtxo[]
*/
public function all(): array
{
$storeWalletUtxos = [];
foreach ($this->getData() as $storeWalletUtxo) {
$storeWalletUtxos[] = new StoreOnChainWalletUtxo($storeWalletUtxo);
}
return $storeWalletUtxos;
}
}

View File

@ -13,40 +13,17 @@ class StorePaymentMethodCollection extends AbstractListResult
{
$r = [];
foreach ($this->getData() as $paymentMethod => $paymentMethodData) {
// BTCPay 2.0 compatibility: List is not a keyed array anymore so fix it here.
if (is_numeric($paymentMethod)) {
$paymentMethod = $paymentMethodData['paymentMethodId'];
// Extract the cryptoCode from the paymentMethodId. e.g. "BTC-CHAIN" -> "BTC"
$parts = explode('-', $paymentMethod);
$extractedCryptoCode = $parts[0];
}
// Consistency: Flatten the array to be consistent with the specific
// payment method endpoints.
if (isset($paymentMethodData['data'])) {
$paymentMethodData += $paymentMethodData['data'];
unset($paymentMethodData['data']);
}
$paymentMethodData += $paymentMethodData['data'];
unset($paymentMethodData['data']);
// BTCPay 2.0 compatibility: Handle config data if exists.
if (isset($paymentMethodData['config'])) {
$paymentMethodData += $paymentMethodData['config'];
unset($paymentMethodData['config']);
}
// BTCPay 2.0 compatibility: Check for renamed LN payment method id.
if (preg_match('/(LightningNetwork|-LN$)/', $paymentMethod)) {
if (strpos($paymentMethod, 'LightningNetwork') !== false) {
// Consistency: Add back the cryptoCode missing on this endpoint
// results until it is there.
if (!isset($paymentMethodData['cryptoCode'])) {
$paymentMethodData['cryptoCode'] = str_replace('-LightningNetwork', '', $paymentMethod);
}
// BTCPay 2.0 compatibility: put the extracted cryptoCode in the cryptoCode field.
if (isset($extractedCryptoCode)) {
$paymentMethodData['cryptoCode'] = $extractedCryptoCode;
}
$r[] = new StorePaymentMethodLightningNetwork($paymentMethodData, $paymentMethod);
} else {
// Consistency: Add back the cryptoCode missing on this endpoint
@ -54,12 +31,6 @@ class StorePaymentMethodCollection extends AbstractListResult
if (!isset($paymentMethodData['cryptoCode'])) {
$paymentMethodData['cryptoCode'] = $paymentMethod;
}
// BTCPay 2.0 compatibility: put the currency code in the cryptoCode field.
if (isset($extractedCryptoCode)) {
$paymentMethodData['cryptoCode'] = $extractedCryptoCode;
}
$r[] = new StorePaymentMethodOnChain($paymentMethodData, $paymentMethod);
}
}

View File

@ -1,93 +0,0 @@
<?php
declare(strict_types=1);
namespace BTCPayServer\Result;
class Subscriber extends AbstractResult
{
public function getCreated(): int
{
return $this->getData()['created'];
}
public function getCustomer(): Customer
{
return new Customer($this->getData()['customer']);
}
public function getOffering(): Offering
{
return new Offering($this->getData()['offering']);
}
public function getPlan(): OfferingPlan
{
return new OfferingPlan($this->getData()['plan']);
}
public function getPeriodEnd(): ?int
{
return $this->getData()['periodEnd'] ?? null;
}
public function getTrialEnd(): ?int
{
return $this->getData()['trialEnd'] ?? null;
}
public function getGracePeriodEnd(): ?int
{
return $this->getData()['gracePeriodEnd'] ?? null;
}
public function isActive(): bool
{
return $this->getData()['isActive'];
}
public function isSuspended(): bool
{
return $this->getData()['isSuspended'];
}
public function getSuspensionReason(): ?string
{
return $this->getData()['suspensionReason'] ?? null;
}
public function isAutoRenew(): bool
{
return $this->getData()['autoRenew'];
}
public function getMetadata(): ?array
{
return $this->getData()['metadata'] ?? null;
}
public function getProcessingInvoiceId(): ?string
{
return $this->getData()['processingInvoiceId'] ?? null;
}
public function getNextPlan(): ?OfferingPlan
{
return isset($this->getData()['nextPlan']) ? new OfferingPlan($this->getData()['nextPlan']) : null;
}
public function getScheduledPlan(): ?OfferingPlan
{
return isset($this->getData()['scheduledPlan']) ? new OfferingPlan($this->getData()['scheduledPlan']) : null;
}
public function getScheduledPlanActivatesAt(): ?int
{
return $this->getData()['scheduledPlanActivatesAt'] ?? null;
}
public function getPhase(): string
{
return $this->getData()['phase'];
}
}

View File

@ -32,7 +32,7 @@ class BaseTest extends TestCase
$this->storeId = $_ENV['BTCPAY_STORE_ID'];
}
public function testThatAllTheVariablesAreSet(): void
public function testItSetsAllTheEnvironmentVariables(): void
{
$this->assertIsString($this->apiKey);
$this->assertIsString($this->host);
@ -44,4 +44,10 @@ class BaseTest extends TestCase
$this->assertNotEmpty($this->storeId);
$this->assertNotEmpty($this->nodeUri);
}
public function dd($var): void
{
var_dump($var);
die();
}
}

27
tests/HealthTest.php Normal file
View File

@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace BTCPayServer\Tests;
use BTCPayServer\Client\Health;
use BTCPayServer\Result\Health as ResultHealth;
final class HealthTest extends BaseTest
{
public function setUp(): void
{
parent::setUp();
}
/** @group getHealthStatus */
public function testItCanGetHealthStatus(): void
{
$healthClient = new Health($this->host, $this->apiKey);
$health = $healthClient->getHealthStatus();
$this->assertInstanceOf(ResultHealth::class, $health);
$this->assertIsBool($health->isSyncronized());
}
}

215
tests/InvoiceTest.php Normal file
View File

@ -0,0 +1,215 @@
<?php
declare(strict_types=1);
namespace BTCPayServer\Tests;
use BTCPayServer\Client\Invoice;
use BTCPayServer\Client\InvoiceCheckoutOptions;
use BTCPayServer\Result\Invoice as ResultInvoice;
use BTCPayServer\Result\InvoicePaymentMethod;
use BTCPayServer\Util\PreciseNumber;
final class InvoiceTest extends BaseTest
{
private ResultInvoice $resultInvoice;
private Invoice $invoiceClient;
private InvoiceCheckoutOptions $invoiceCheckoutOptions;
public function setUp(): void
{
parent::setUp();
$this->invoiceClient = new Invoice($this->host, $this->apiKey);
$this->invoiceCheckoutOptions = new InvoiceCheckoutOptions();
$this->invoiceCheckoutOptions->setRedirectAutomatically(true);
$this->invoiceCheckoutOptions->setRedirectURL('https://example.com/redirect');
$this->invoiceCheckoutOptions->setDefaultLanguage('EN');
$this->invoiceCheckoutOptions->setPaymentMethods(['BTC-LightningNetwork']);
$this->invoiceCheckoutOptions->setRequiresRefundEmail(true);
$this->resultInvoice = $this->invoiceClient->createInvoice(
storeId: $this->storeId,
currency: 'SATS',
amount: PreciseNumber::parseString('1000'),
orderId: '1234',
buyerEmail: 'testing@btcpayserver.org',
metaData: [
'user_id' => '123456789',
],
checkoutOptions: $this->invoiceCheckoutOptions,
);
}
/** @group createInvoice */
public function testItCanCreateAnInvoiceAndSetCheckoutOptions(): void
{
$this->assertInvoiceGettersAreSet($this->resultInvoice);
}
/** @group getInvoice */
public function testItCanGetAnInvoiceWithCheckoutOptions(): void
{
$invoice = $this->invoiceClient->getInvoice(
storeId: $this->storeId,
invoiceId: $this->resultInvoice->getId()
);
$this->assertInvoiceGettersAreSet($invoice);
}
private function assertInvoiceGettersAreSet(ResultInvoice $invoice): void
{
$this->assertInstanceOf(ResultInvoice::class, $invoice);
$this->assertIsString($invoice->getId());
$this->assertIsString($invoice->getStoreId());
$this->assertIsString($invoice->getCurrency());
$this->assertIsString($invoice->getType());
$this->assertIsString($invoice->getCheckoutLink());
$this->assertIsInt($invoice->getCreatedTime());
$this->assertIsInt($invoice->getExpirationTime());
$this->assertIsInt($invoice->getMonitoringExpiration());
$this->assertisString($invoice->getAdditionalStatus());
$this->assertIsString($invoice->getStatus());
$this->assertIsArray($invoice->getMetaData());
// Checkout Options Assertions
$this->assertInstanceOf(InvoiceCheckoutOptions::class, $invoice->getCheckoutOptions());
$this->assertIsArray($invoice->getCheckoutOptions()->getPaymentMethods());
// Assert that RedirectURL is either null or string
if ($invoice->getCheckoutOptions()->getRedirectURL() !== null) {
$this->assertIsString($invoice->getCheckoutOptions()->getRedirectURL());
} else {
$this->assertNull($invoice->getCheckoutOptions()->getRedirectURL());
}
$this->assertIsInt($invoice->getCheckoutOptions()->getExpirationMinutes());
$this->assertIsInt($invoice->getCheckoutOptions()->getMonitoringMinutes());
$this->assertIsFloat($invoice->getCheckoutOptions()->getPaymentTolerance());
$this->assertIsBool($invoice->getCheckoutOptions()->isRedirectAutomatically());
// Assert that DefaultLanguage is either null or bool
if ($invoice->getCheckoutOptions()->getDefaultLanguage() !== null) {
$this->assertIsString($invoice->getCheckoutOptions()->getDefaultLanguage());
} else {
$this->assertNull($invoice->getCheckoutOptions()->getDefaultLanguage());
}
}
public function testItCanGetInvoicePaymentMethodsAndGetters(): void
{
$paymentMethods = $this->invoiceClient->getPaymentMethods(
storeId: $this->storeId,
invoiceId: $this->resultInvoice->getId()
);
$this->assertIsArray($paymentMethods);
foreach ($paymentMethods as $paymentMethod) {
$this->assertInstanceOf(InvoicePaymentMethod::class, $paymentMethod);
$this->assertIsArray($paymentMethod->getPayments());
$this->assertIsString($paymentMethod->getPaymentMethod());
$this->assertIsString($paymentMethod->getDestination());
$this->assertIsString($paymentMethod->getRate());
$this->assertIsString($paymentMethod->getPaymentMethodPaid());
$this->assertIsString($paymentMethod->getTotalPaid());
$this->assertIsString($paymentMethod->getDue());
$this->assertIsString($paymentMethod->getAmount());
$this->assertIsString($paymentMethod->getNetworkFee());
$this->assertIsString($paymentMethod->getCryptoCode());
}
}
public function testItCanUpdateAnInvoice(): void
{
$invoice = $this->invoiceClient->updateInvoice(
storeId: $this->storeId,
invoiceId: $this->resultInvoice->getId(),
metaData: [
'user_id' => '987654321',
],
);
$this->assertIsArray($invoice->getMetaData());
$this->assertEquals('987654321', $invoice->getMetaData()['user_id']);
$this->assertInvoiceGettersAreSet($invoice);
}
public function testItCanMarkInvoiceStatus(): void
{
$invoice = $this->invoiceClient->markInvoiceStatus(
storeId: $this->storeId,
invoiceId: $this->resultInvoice->getId(),
markAs: 'Invalid'
);
$this->assertInvoiceGettersAreSet($invoice);
$this->assertEquals('Invalid', $invoice->getStatus());
$invoice = $this->invoiceClient->markInvoiceStatus(
storeId: $this->storeId,
invoiceId: $this->resultInvoice->getId(),
markAs: 'Settled'
);
$this->assertEquals('Settled', $invoice->getStatus());
}
public function testItCanArchiveAndUnarchiveAnInvoice(): void
{
$archiveInvoice = $this->invoiceClient->archiveInvoice(
storeId: $this->storeId,
invoiceId: $this->resultInvoice->getId()
);
$this->assertTrue($archiveInvoice);
// Get the invoice again as a new object with hydrated properties.
$invoice = $this->invoiceClient->getInvoice(
storeId: $this->storeId,
invoiceId: $this->resultInvoice->getId()
);
$this->assertTrue($invoice->isArchived());
$unarchiveInvoice = $this->invoiceClient->unarchiveInvoice(
storeId: $this->storeId,
invoiceId: $this->resultInvoice->getId()
);
$this->assertTrue($unarchiveInvoice);
// Get the invoice again as a new object with hydrated properties.
$invoice = $this->invoiceClient->getInvoice(
storeId: $this->storeId,
invoiceId: $this->resultInvoice->getId()
);
$this->assertFalse($invoice->isArchived());
}
public function testItCanActivatePaymentMethod(): void
{
$activatePaymentMethod = $this->invoiceClient->activatePaymentMethod(
storeId: $this->storeId,
invoiceId: $this->resultInvoice->getId(),
paymentMethod: 'BTC-LightningNetwork'
);
$this->assertTrue($activatePaymentMethod);
}
public function tearDown(): void
{
$achivedInvoice = $this->invoiceClient->archiveInvoice(
storeId: $this->storeId,
invoiceId: $this->resultInvoice->getId()
);
$this->assertIsBool($achivedInvoice);
}
}

View File

@ -0,0 +1,197 @@
<?php
declare(strict_types=1);
namespace BTCPayServer\Tests;
use BTCPayServer\Client\LightningStore;
use BTCPayServer\Result\LightningChannel;
use BTCPayServer\Result\LightningChannelList;
use BTCPayServer\Result\LightningInvoice;
use BTCPayServer\Result\LightningPayment;
use BTCPayServer\Util\PreciseNumber;
use Exception;
final class LightningStoreTest extends BaseTest
{
protected LightningStore $lightningStoreClient;
public function setUp(): void
{
parent::setUp();
$this->lightningStoreClient = new LightningStore($this->host, $this->apiKey);
}
/** @group createLightningInvoice */
public function testItCanCreateALightningInvoiceAndReturnsLightningInvoiceObject(): void
{
$lightningInvoice = $this->lightningStoreClient->createLightningInvoice(
cryptoCode: 'BTC',
storeId: $this->storeId,
amount: '100000', // milisats
expiry: 111111,
description: 'Test invoice description',
);
$this->assertInstanceOf(LightningInvoice::class, $lightningInvoice);
$this->lightningInvoice = $lightningInvoice;
$this->assertIsString($lightningInvoice->getId());
$this->assertIsString($lightningInvoice->getStatus());
$this->assertIsString($lightningInvoice->getBolt11());
// If the lightning invoice is paid, assert the paid at is an int
if ($lightningInvoice->getPaidAt()) {
$this->assertIsInt($lightningInvoice->getPaidAt());
}
$this->assertIsInt($lightningInvoice->getExpiresAt());
$this->assertInstanceOf(PreciseNumber::class, $lightningInvoice->getAmount());
// If the lightning invoice is paid, assert the amount received is a PreciseNumber
if ($lightningInvoice->getAmountReceived()) {
$this->assertInstanceOf(PreciseNumber::class, $lightningInvoice->getAmountReceived());
}
}
/** @group payLightningInvoice */
public function testItReceivesLightningPaymentObjectAfterPayingLightningInvoiceWithAllGetters(): void
{
$this->markTestSkipped('Requires a new invoice on each test run');
$bolt11 = '';
$lightningPayment = $this->lightningStoreClient->payLightningInvoice(
cryptoCode: 'BTC',
storeId: $this->storeId,
BOLT11: $bolt11,
maxFeePercent: 0.1,
maxFeeFlat: 100,
);
$this->assertInstanceOf(LightningPayment::class, $lightningPayment);
// There is a bug in Greenfield API that is returning null values on everything except total and fee amounts.
// Uncomment these lines when the bug is fixed.
// https://github.com/btcpayserver/btcpayserver/issues/4229
// $this->assertIsString($lightningPayment->getId());
// $this->assertIsString($lightningPayment->getStatus());
// $this->assertIsString($lightningPayment->getBolt11());
// $this->assertIsString($lightningPayment->getPaymentHash());
// $this->assertIsString($lightningPayment->getPreimage());
// $this->assertIsInt($lightningPayment->getCreatedAt());
// $this->assertInstanceOf(PreciseNumber::class, $lightningPayment->getTotalAmount());
// $this->assertInstanceOf(PreciseNumber::class, $lightningPayment->getFeeAmount());
}
/** @group connectToLightningNode */
public function testItCanConnectToALightningNodeAndReturnsLightningNodeConnectionObject(): void
{
$this->markTestSkipped('This test is skipped because I always get 503.');
try {
$lightningNodeConnection = $this->lightningStoreClient->connectToLightningNode(
cryptoCode: 'BTC',
storeId: $this->storeId,
nodeURI: $this->nodeUri,
);
$this->assertInstanceOf(LightningNodeConnection::class, $lightningNodeConnection);
} catch (Exception $e) {
die($e->getMessage());
}
}
/** @group getNodeInformation */
public function testItCanGetNodeInformationAndReturnsLightningNodeInformationObject(): void
{
$lightningNodeInformation = $this->lightningStoreClient->getNodeInformation(
cryptoCode: 'BTC',
storeId: $this->storeId,
);
$this->assertInstanceOf(\BTCPayServer\Result\LightningNode::class, $lightningNodeInformation);
$this->assertIsArray($lightningNodeInformation->getNodeURIs());
$this->assertIsInt($lightningNodeInformation->getBlockHeight());
$this->assertIsString($lightningNodeInformation->getAlias());
$this->assertIsString($lightningNodeInformation->getColor());
$this->assertIsString($lightningNodeInformation->getVersion());
$this->assertIsInt($lightningNodeInformation->getPeersCount());
$this->assertIsInt($lightningNodeInformation->getPendingChannelsCount());
$this->assertIsInt($lightningNodeInformation->getActiveChannelsCount());
$this->assertIsInt($lightningNodeInformation->getInactiveChannelsCount());
}
/** @group getChannels */
public function testItCanGetChannelsAndReturnsLightningChannelListObject(): void
{
$lightningChannels = $this->lightningStoreClient->getChannels(
cryptoCode: 'BTC',
storeId: $this->storeId,
);
$this->assertInstanceOf(LightningChannelList::class, $lightningChannels);
$this->assertIsArray($lightningChannels->all());
foreach ($lightningChannels->all() as $channel) {
$this->assertInstanceOf(LightningChannel::class, $channel);
$this->assertIsString($channel->getRemoteNode());
$this->assertIsString($channel->getChannelPoint());
$this->assertIsString($channel->getCapacity());
$this->assertIsString($channel->getLocalBalance());
$this->assertIsBool($channel->isActive());
$this->assertIsBool($channel->isPublic());
}
}
/** @group getDepositAddress */
public function testItCanGetANewDepositAddress(): void
{
$depositAddress = $this->lightningStoreClient->getDepositAddress(
cryptoCode: 'BTC',
storeId: $this->storeId,
);
$this->assertIsString($depositAddress);
}
/** @group getLightningInvoice */
public function testItCanGetAnInvoiceAndReturnsLightningInvoiceObject(): void
{
$getLightningInvoice = $this->lightningStoreClient->createLightningInvoice(
cryptoCode: 'BTC',
storeId: $this->storeId,
amount: '100000', // milisats
expiry: 111111,
description: 'Test invoice description',
);
$lightningInvoice = $this->lightningStoreClient->getLightningInvoice(
'BTC',
$this->storeId,
$getLightningInvoice->getId(),
);
$this->assertInstanceOf(LightningInvoice::class, $lightningInvoice);
$this->assertIsString($lightningInvoice->getId());
$this->assertIsString($lightningInvoice->getStatus());
$this->assertIsString($lightningInvoice->getBolt11());
// If the invoice get Paid at is not null, assert it's int
if ($lightningInvoice->getPaidAt() !== null) {
$this->assertIsInt($lightningInvoice->getPaidAt());
}
$this->assertIsInt($lightningInvoice->getExpiresAt());
$this->assertInstanceOf(PreciseNumber::class, $lightningInvoice->getAmount());
// If the invoice get Paid amount is not null, assert it's PreciseNumber
if ($lightningInvoice->getAmountReceived() !== null) {
$this->assertInstanceOf(PreciseNumber::class, $lightningInvoice->getAmountReceived());
}
}
}

View File

@ -0,0 +1,68 @@
<?php
declare(strict_types=1);
namespace BTCPayServer\Tests;
use BTCPayServer\Client\Invoice;
use BTCPayServer\Client\Miscellaneous;
use BTCPayServer\Result\InvoiceCheckoutHtml;
use BTCPayServer\Result\LanguageCode;
use BTCPayServer\Result\LanguageCodeList;
use BTCPayServer\Result\PermissionMetadata;
use BTCPayServer\Result\PermissionMetadataList;
use BTCPayServer\Util\PreciseNumber;
final class MiscellaneousTest extends BaseTest
{
private Miscellaneous $miscellaneousClient;
public function setUp(): void
{
parent::setUp();
$this->miscellaneousClient = new Miscellaneous($this->host, $this->apiKey);
}
public function testItCanGetPermissionMetadata(): void
{
$result = $this->miscellaneousClient->getPermissionMetadata();
$this->assertInstanceOf(PermissionMetadataList::class, $result);
foreach ($result->all() as $permissionMetadata) {
$this->assertInstanceOf(PermissionMetadata::class, $permissionMetadata);
$this->assertIsString($permissionMetadata->getName());
$this->assertIsArray($permissionMetadata->getIncluded());
}
}
public function testItCanGetLanguageCodes(): void
{
$result = $this->miscellaneousClient->getLanguageCodes();
$this->assertInstanceOf(LanguageCodeList::class, $result);
foreach ($result->all() as $languageCode) {
$this->assertInstanceOf(LanguageCode::class, $languageCode);
$this->assertIsString($languageCode->getCode());
$this->assertIsString($languageCode->getCurrentLanguage());
}
}
public function testItCanGetInvoiceCheckout(): void
{
$invoiceClient = new Invoice($this->host, $this->apiKey);
$invoice = $invoiceClient->createInvoice(
storeId: $this->storeId,
currency: 'SATS',
amount: PreciseNumber::parseString('1000'),
);
$result = $this->miscellaneousClient->getInvoiceCheckout($invoice->getId(), null);
$this->assertInstanceOf(InvoiceCheckoutHtml::class, $result);
$this->assertStringContainsString('<!DOCTYPE html>', $result->getHtml());
}
}

View File

@ -0,0 +1,83 @@
<?php
declare(strict_types=1);
namespace BTCPayServer\Tests;
use BTCPayServer\Client\Notification;
use BTCPayServer\Result\NotificationList;
final class NotificationTest extends BaseTest
{
public function setUp(): void
{
parent::setUp();
$this->notificationClient = new Notification($this->host, $this->apiKey);
}
/** @group getNotifications */
public function testItCanGetNotifications(): void
{
$notifications = $this->notificationClient->getNotifications();
$this->assertInstanceOf(NotificationList::class, $notifications);
foreach ($notifications->all() as $notification) {
$this->assertIsString($notification->getId());
$this->assertIsString($notification->getBody());
$this->assertIsString($notification->getLink());
$this->assertIsInt($notification->getCreatedTime());
$this->assertIsBool($notification->isSeen());
}
}
/** @group getNotification */
public function testItCanGetNotification(): void
{
$notifications = $this->notificationClient->getNotifications();
if (empty($notifications->all())) {
$this->markTestSkipped('No notifications found');
}
$notification = $this->notificationClient->getNotification($notifications->all()[0]->getId());
$this->assertIsString($notification->getId());
$this->assertIsString($notification->getBody());
$this->assertIsString($notification->getLink());
$this->assertIsInt($notification->getCreatedTime());
$this->assertIsBool($notification->isSeen());
}
/** @group updateNotification */
public function testItCanUpdateNotification(): void
{
$notifications = $this->notificationClient->getNotifications();
if (empty($notifications->all())) {
$this->markTestSkipped('No notifications found');
}
$notification = $this->notificationClient->updateNotification($notifications->all()[0]->getId(), true);
$this->assertIsString($notification->getId());
$this->assertIsString($notification->getBody());
$this->assertIsString($notification->getLink());
$this->assertIsInt($notification->getCreatedTime());
$this->assertTrue($notification->isSeen());
}
/** @group removeNotification */
public function testItCanRemoveNotification(): void
{
$notifications = $this->notificationClient->getNotifications();
if (empty($notifications->all())) {
$this->markTestSkipped('No notifications found');
}
$notification = $this->notificationClient->removeNotification($notifications->all()[0]->getId());
$this->assertTrue($notification);
}
}

190
tests/PullPaymentTest.php Normal file
View File

@ -0,0 +1,190 @@
<?php
declare(strict_types=1);
namespace BTCPayServer\Tests;
use BTCPayServer\Client\PullPayment;
use BTCPayServer\Result\PullPayment as ResultPullPayment;
use BTCPayServer\Result\PullPaymentList;
use BTCPayServer\Result\PullPaymentPayout;
use BTCPayServer\Util\PreciseNumber;
final class PullPaymentTest extends BaseTest
{
public PullPayment $pullPaymentClient;
public function setUp(): void
{
parent::setUp();
$this->pullPaymentClient = new PullPayment($this->host, $this->apiKey);
}
/** @group createPullPayment */
public function testItCanCreatePullPayment(): void
{
$pullPayment = $this->pullPaymentClient->createPullPayment(
storeId: $this->storeId,
name: 'Test Pull Payment',
amount: PreciseNumber::parseString('100'),
currency: 'SATS',
period: 30,
BOLT11Expiration: 30,
autoApproveClaims: false,
startsAt: time(),
expiresAt: time() + 30,
paymentMethods: ['BTC-LightningNetwork']
);
$this->assertInstanceOf(ResultPullPayment::class, $pullPayment);
$this->assertNotEmpty($pullPayment->getId());
$this->assertEquals('Test Pull Payment', $pullPayment->getName());
$this->assertEquals('SATS', $pullPayment->getCurrency());
$this->assertEquals('100', $pullPayment->getAmount());
$this->assertEquals(30, $pullPayment->getPeriod());
$this->assertEquals(30, $pullPayment->getBOLT11Expiration());
$this->assertFalse($pullPayment->isArchived());
$this->assertNotEmpty($pullPayment->getViewLink());
}
/** @group getStorePullPayments */
public function testGetStorePullPayments(): void
{
$pullPayments = $this->pullPaymentClient->getStorePullPayments($this->storeId, false);
$this->assertInstanceOf(PullPaymentList::class, $pullPayments);
$this->assertNotEmpty($pullPayments->all());
$this->assertIsArray($pullPayments->all());
foreach ($pullPayments->all() as $pullPayment) {
$this->assertInstanceOf(ResultPullPayment::class, $pullPayment);
$this->assertIsString($pullPayment->getId());
$this->assertIsString($pullPayment->getName());
$this->assertIsString($pullPayment->getCurrency());
$this->assertInstanceOf(PreciseNumber::class, $pullPayment->getAmount());
$this->assertIsInt($pullPayment->getPeriod());
$this->assertIsInt($pullPayment->getBOLT11Expiration());
$this->assertIsBool($pullPayment->isArchived());
$this->assertIsString($pullPayment->getViewLink());
}
}
/** @group getPullPayment */
public function testGetPullPayment(): void
{
$pullPayments = $this->pullPaymentClient->getStorePullPayments($this->storeId, false);
$pullPayment = $this->pullPaymentClient->getPullPayment($pullPayments->all()[0]->getId());
$this->assertInstanceOf(ResultPullPayment::class, $pullPayment);
$this->assertIsString($pullPayment->getId());
$this->assertIsString($pullPayment->getName());
$this->assertIsString($pullPayment->getCurrency());
$this->assertInstanceOf(PreciseNumber::class, $pullPayment->getAmount());
$this->assertIsInt($pullPayment->getPeriod());
$this->assertIsInt($pullPayment->getBOLT11Expiration());
$this->assertIsBool($pullPayment->isArchived());
$this->assertIsString($pullPayment->getViewLink());
}
/** @group archivePullPayment */
public function testArchivePullPayment(): void
{
// Grab the first unarchived pull payment and archive it.
$pullPayment = $this->pullPaymentClient->getStorePullPayments($this->storeId, false)->all()[0];
$this->pullPaymentClient->archivePullPayment($this->storeId, $pullPayment->getId());
// Get All Pull Payments, including archived, as an array.
$allPullPayments = $this->pullPaymentClient->getStorePullPayments($this->storeId, true)->all();
// Find the the archived pull payment by id.
$archivedPullPayment = array_filter($allPullPayments, function ($pullPayment) {
return $pullPayment->getId() === $pullPayment->getId();
});
// Assert that the pull payment is archived.
$this->assertTrue($archivedPullPayment[0]->isArchived());
}
/** @group payouts */
public function testItCanGetAllPayoutMethods(): void
{
// Create a pull payment.
$pullPayment = $this->pullPaymentClient->createPullPayment(
storeId: $this->storeId,
name: 'Test Pull Payment',
amount: PreciseNumber::parseFloat(0.00001),
currency: 'BTC',
period: 1,
BOLT11Expiration: 1,
autoApproveClaims: false,
startsAt: time(),
expiresAt: time() + 100,
paymentMethods: ['BTC-LightningNetwork']
);
$lightningClient = new \BTCPayServer\Client\LightningInternalNode($this->host, $this->apiKey);
$lightningInvoice = $lightningClient->createLightningInvoice(
'BTC',
'1000000', // milisats
111111,
'Test invoice description',
);
$bolt11 = $lightningInvoice["BOLT11"];
// Create a payout associated with the pull payment.
$payout = $this->pullPaymentClient->createPayout(
pullPaymentId: $pullPayment->getId(),
destination: $bolt11,
amount: PreciseNumber::parseFloat(0.00001),
paymentMethod: 'BTC-LightningNetwork'
);
$this->assertInstanceOf(PullPaymentPayout::class, $payout);
$this->assertIsString($payout->getId());
$this->assertIsInt($payout->getRevision());
$this->assertIsString($payout->getPullPaymentId());
$this->assertIsInt($payout->getDate());
$this->assertIsString($payout->getDestination());
$this->assertInstanceOf(PreciseNumber::class, $payout->getAmount());
$this->assertIsString($payout->getPaymentMethod());
$this->assertIsString($payout->getCryptoCode());
$this->assertIsString($payout->getState());
$this->assertEquals('AwaitingApproval', $payout->getState());
// If PaymentMethodAmount is not null, assert it's an int.
if ($payout->getPaymentMethodAmount() !== null) {
$this->assertIsInt($payout->getPaymentMethodAmount());
}
// Test that we can get the payout.
$getPayout = $this->pullPaymentClient->getPayout($pullPayment->getId(), $payout->getId());
// Assert that the payout is the same as the one we created.
$this->assertEquals($payout, $getPayout);
// Approve the payout.
$approve = $this->pullPaymentClient->approvePayout(
storeId: $this->storeId,
payoutId: $payout->getId(),
revision: $payout->getRevision(),
rateRule: null,
);
$this->assertInstanceOf(PullPaymentPayout::class, $approve);
$this->assertEquals('AwaitingPayment', $approve->getState());
// Mark the Payout as Paid.
$paid = $this->pullPaymentClient->markPayoutAsPaid(
storeId: $this->storeId,
payoutId: $payout->getId(),
);
$this->assertTrue($paid);
// Archive the new pull payment.
$archive = $this->pullPaymentClient->archivePullPayment($this->storeId, $pullPayment->getId());
$this->assertTrue($archive);
}
}

49
tests/ServerTest.php Normal file
View File

@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
namespace BTCPayServer\Tests;
use BTCPayServer\Client\Server;
use BTCPayServer\Result\ServerInfo;
use BTCPayServer\Result\ServerSyncStatusList;
use BTCPayServer\Result\ServerSyncStatusNodeInformation;
use BTCPayServer\Util\PreciseNumber;
final class ServerTest extends BaseTest
{
public Server $serverClient;
public function setUp(): void
{
parent::setUp();
$this->serverClient = new Server($this->host, $this->apiKey);
}
/** @group getServerInfo */
public function testItGetsServerInfoAndAllGetters(): void
{
$serverInfo = $this->serverClient->getInfo();
$this->assertInstanceOf(ServerInfo::class, $serverInfo);
$this->assertIsString($serverInfo->getVersion());
$this->assertIsString($serverInfo->getOnionUrl());
$this->assertIsBool($serverInfo->isFullySynced());
$this->assertIsArray($serverInfo->getSupportedPaymentMethods());
$this->assertInstanceOf(ServerSyncStatusList::class, $serverInfo->getSyncStatus());
foreach ($serverInfo->getSyncStatus()->all() as $serverSyncStatus) {
$this->assertIsInt($serverSyncStatus->getChainHeight());
$this->assertIsInt($serverSyncStatus->getSyncHeight());
$this->assertIsString($serverSyncStatus->getCryptoCode());
$this->assertIsBool($serverSyncStatus->isAvailable());
$this->assertInstanceOf(ServerSyncStatusNodeInformation::class, $serverSyncStatus->getNodeInformation());
$this->assertIsInt($serverSyncStatus->getNodeInformation()->getHeaders());
$this->assertIsInt($serverSyncStatus->getNodeInformation()->getBlocks());
$this->assertInstanceOf(PreciseNumber::class, $serverSyncStatus->getNodeInformation()->getVerificationProgress());
}
}
}

114
tests/StoreEmailTest.php Normal file
View File

@ -0,0 +1,114 @@
<?php
declare(strict_types=1);
namespace BTCPayServer\Tests;
use BTCPayServer\Client\Store;
use BTCPayServer\Client\StoreEmail;
use BTCPayServer\Result\Store as ResultStore;
use BTCPayServer\Result\StoreEmailSettings;
final class StoreEmailTest extends BaseTest
{
public Store $storeClient;
public ResultStore $store;
public StoreEmail $storeEmailClient;
public function setUp(): void
{
parent::setUp();
$this->storeClient = new Store($this->host, $this->apiKey);
$this->store = $this->storeClient->createStore(
name: 'Test Store',
website: 'https://example.com',
defaultCurrency: 'USD',
invoiceExpiration: 900,
displayExpirationTimer: 300,
monitoringExpiration: 3600,
speedPolicy: 'MediumSpeed',
lightningDescriptionTemplate: null,
paymentTolerance: 0,
anyoneCanCreateInvoice: false,
requiresRefundEmail: false,
checkoutType: 'V1',
receipt: null,
lightningAmountInSatoshi: false,
lightningPrivateRouteHints: false,
onChainWithLnInvoiceFallback: false,
redirectAutomatically: false,
showRecommendedFee: true,
recommendedFeeBlockTarget: 1,
defaultLang: 'en',
customLogo: 'https://test.com',
customCSS: 'auto: 100px;',
htmlTitle: 'the best store ever',
networkFeeMode: 'MultiplePaymentsOnly',
payJoinEnabled: false,
lazyPaymentMethods: false,
defaultPaymentMethod: 'BTC'
);
$this->storeEmailClient = new StoreEmail($this->host, $this->apiKey);
}
/** @group getSettings */
public function testItCanGetEmailSettings(): void
{
$storeEmailSettings = $this->storeEmailClient->getSettings($this->store->getId());
$this->assertEmailSettingsGettersAreSet($storeEmailSettings);
}
/** @group updateSettings */
public function testItCanUpdateEmailSettings(): void
{
$storeEmailSettings = $this->storeEmailClient->updateSettings(
$this->store->getId(),
server: 'smtp.example.com',
port: 587,
username: 'tester',
password: 'password',
fromEmail: 'tester@btcpayserver.org',
fromName: 'Tester',
disableCertificateCheck: false,
);
$this->assertEmailSettingsGettersAreSet($storeEmailSettings);
}
/** @group sendMail */
public function testItCanSendMail(): void
{
$email = $this->storeEmailClient->sendMail(
storeId: $this->store->getId(),
email: 'testing@btcpayserver.org',
subject: 'Test Email',
body: 'This is a test email',
);
$this->assertTrue($email);
}
private function assertEmailSettingsGettersAreSet($storeEmailSettings): void
{
$this->assertInstanceOf(StoreEmailSettings::class, $storeEmailSettings);
if ($storeEmailSettings->getServer()) {
$this->assertIsString($storeEmailSettings->getServer());
}
if ($storeEmailSettings->getPort()) {
$this->assertIsInt($storeEmailSettings->getPort());
}
if ($storeEmailSettings->getUsername()) {
$this->assertIsString($storeEmailSettings->getUsername());
}
if ($storeEmailSettings->getPassword()) {
$this->assertIsString($storeEmailSettings->getPassword());
}
if ($storeEmailSettings->getFromEmail()) {
$this->assertIsString($storeEmailSettings->getFromEmail());
}
// @TODO: Re-enable when bug is fixed - https://github.com/btcpayserver/btcpayserver/issues/5139
// if ($storeEmailSettings->getFromName()) $this->assertIsString($storeEmailSettings->getFromName());
}
}

View File

@ -0,0 +1,198 @@
<?php
declare(strict_types=1);
namespace BTCPayServer\Tests;
use BTCPayServer\Client\Store;
use BTCPayServer\Client\StoreOnChainWallet;
use BTCPayServer\Result\StoreOnChainWallet as ResultStoreOnChainWallet;
use BTCPayServer\Result\StoreOnChainWalletAddress;
use BTCPayServer\Result\StoreOnChainWalletFeeRate;
use BTCPayServer\Result\StoreOnChainWalletTransaction;
use BTCPayServer\Result\StoreOnChainWalletTransactionList;
use BTCPayServer\Result\StoreOnChainWalletUtxo;
use BTCPayServer\Result\StoreOnChainWalletUtxoList;
use BTCPayServer\Util\PreciseNumber;
final class StoreOnChainWalletTest extends BaseTest
{
public Store $storeClient;
public StoreOnChainWallet $storeOnChainWalletClient;
public function setUp(): void
{
parent::setUp();
$this->storeClient = new Store($this->host, $this->apiKey);
$this->storeOnChainWalletClient = new StoreOnChainWallet($this->host, $this->apiKey);
}
/** @group getStoreOnChainWalletOverview */
public function testItCanGetStoreOnChainWalletOverview(): void
{
//$this->markTestIncomplete('BTC doesnt have any derivation scheme set');
$overview = $this->storeOnChainWalletClient->getStoreOnChainWalletOverview(
$this->storeId,
'BTC'
);
$this->assertInstanceOf(ResultStoreOnChainWallet::class, $overview);
$this->assertInstanceOf(PreciseNumber::class, $overview->getBalance());
$this->assertInstanceOf(PreciseNumber::class, $overview->getUnconfirmedBalance());
$this->assertInstanceOf(PreciseNumber::class, $overview->getConfirmedBalance());
$this->assertIsString($overview->getLabel());
}
/** @group getStoreOnChainWalletFeeRate */
public function testItCanGetStoreOnChainWalletFeeRate(): void
{
$feeRate = $this->storeOnChainWalletClient->getStoreOnChainWalletFeeRate(
$this->storeId,
'BTC'
);
$this->assertInstanceOf(StoreOnChainWalletFeeRate::class, $feeRate);
$this->assertInstanceOf(PreciseNumber::class, $feeRate->getFeeRate());
}
/** @group getStoreOnChainWalletAddress */
public function testItCanGetStoreOnChainWalletAddress(): void
{
$address = $this->storeOnChainWalletClient->getStoreOnChainWalletAddress(
$this->storeId,
'BTC'
);
$this->assertInstanceOf(StoreOnChainWalletAddress::class, $address);
$this->assertIsString($address->getAddress());
$this->assertIsString($address->getKeyPath());
$this->assertIsString($address->getPaymentLink());
}
/** @group unReserveLastStoreOnChainWalletAddress */
public function testItCanunReserveLastStoreOnChainWalletAddress(): void
{
$address = $this->storeOnChainWalletClient->unReserveLastStoreOnChainWalletAddress(
$this->storeId,
'BTC'
);
$this->assertIsBool($address);
}
/** @group getStoreOnChainWalletTransactions */
public function testItCanGetStoreOnChainWalletTransactions(): void
{
$transactions = $this->storeOnChainWalletClient->getStoreOnChainWalletTransactions(
$this->storeId,
'BTC'
);
$this->assertInstanceOf(StoreOnChainWalletTransactionList::class, $transactions);
$this->assertIsArray($transactions->all());
foreach ($transactions->all() as $transaction) {
$this->assertInstanceOf(StoreOnChainWalletTransaction::class, $transaction);
$this->assertIsString($transaction->getTransactionHash());
$this->assertIsString($transaction->getComment());
$this->assertIsArray($transaction->getLabels());
$this->assertIsInt($transaction->getConfirmations());
$this->assertIsInt($transaction->getTimestamp());
$this->assertIsString($transaction->getStatus());
}
}
/** @group createStoreOnChainWalletTransaction */
public function testItCanCreateGetUpdateStoreOnChainWalletTransaction(): void
{
$destination =
[
'destination' => 'tb1q2yy5gxpdlsr40xjvy7v6x4gjxr5y8t428nqppa',
'amount' => "0.00001",
'subtractFromAmount' => true,
];
$transaction = $this->storeOnChainWalletClient->createStoreOnChainWalletTransaction(
$this->storeId,
'BTC',
[$destination],
2.0,
false,
true,
false,
);
$this->assertInstanceOf(StoreOnChainWalletTransaction::class, $transaction);
$this->assertIsString($transaction->getTransactionHash());
$this->assertIsString($transaction->getComment());
$this->assertIsArray($transaction->getLabels());
$this->assertIsInt($transaction->getConfirmations());
$this->assertIsInt($transaction->getTimestamp());
$this->assertIsString($transaction->getStatus());
if ($transaction->getBlockHash() !== null) {
$this->assertIsString($transaction->getBlockHash());
}
if ($transaction->getBlockHeight() !== null) {
$this->assertIsInt($transaction->getBlockHeight());
}
$getTransaction = $this->storeOnChainWalletClient->getStoreOnChainWalletTransaction(
$this->storeId,
'BTC',
$transaction->getTransactionHash(),
);
$this->assertInstanceOf(StoreOnChainWalletTransaction::class, $getTransaction);
$this->assertIsString($getTransaction->getTransactionHash());
$this->assertIsString($getTransaction->getComment());
$this->assertIsArray($getTransaction->getLabels());
$this->assertIsInt($getTransaction->getConfirmations());
$this->assertIsInt($getTransaction->getTimestamp());
$this->assertIsString($getTransaction->getStatus());
$updatedTransaction = $this->storeOnChainWalletClient->updateStoreOnChainWalletTransaction(
$this->storeId,
'BTC',
$transaction->getTransactionHash(),
'test comment',
['test label'],
);
$this->assertInstanceOf(StoreOnChainWalletTransaction::class, $updatedTransaction);
$this->assertIsString($updatedTransaction->getTransactionHash());
$this->assertIsString($updatedTransaction->getComment());
$this->assertIsArray($updatedTransaction->getLabels());
$this->assertIsInt($updatedTransaction->getConfirmations());
$this->assertIsInt($updatedTransaction->getTimestamp());
$this->assertIsString($updatedTransaction->getStatus());
$this->assertEquals('test comment', $updatedTransaction->getComment());
}
/** @group getStoreOnChainWalletUtxos */
public function testItCanGetStoreOnChainWalletUtxos(): void
{
$utxos = $this->storeOnChainWalletClient->getStoreOnChainWalletUtxos(
$this->storeId,
'BTC'
);
$this->assertInstanceOf(StoreOnChainWalletUtxoList::class, $utxos);
foreach($utxos as $utxo) {
$this->assertInstanceOf(StoreOnChainWalletUtxo::class, $utxo);
$this->assertIsString($utxo->getComment());
$this->assertIsString($utxo->getAmount());
$this->assertIsString($utxo->getOutpoint());
$this->assertIsString($utxo->getLink());
$this->assertIsArray($utxo->getLabels());
$this->assertIsInt($utxo->getTimestamp());
$this->assertIsString($utxo->getKeyPath());
$this->assertIsString($utxo->getAddress());
$this->assertIsInt($utxo->getConfirmations());
}
}
}

119
tests/StoreTest.php Normal file
View File

@ -0,0 +1,119 @@
<?php
declare(strict_types=1);
namespace BTCPayServer\Tests;
use BTCPayServer\Client\Store;
use BTCPayServer\Result\Store as ResultStore;
use BTCPayServer\Result\StoreList;
use BTCPayServer\Util\PreciseNumber;
final class StoreTest extends BaseTest
{
public Store $storeClient;
public ResultStore $store;
public function setUp(): void
{
parent::setUp();
$this->storeClient = new Store($this->host, $this->apiKey);
$this->store = $this->storeClient->createStore(
name: 'Test Store',
website: 'https://example.com',
defaultCurrency: 'USD',
invoiceExpiration: 900,
displayExpirationTimer: 300,
monitoringExpiration: 3600,
speedPolicy: 'MediumSpeed',
lightningDescriptionTemplate: null,
paymentTolerance: 0,
anyoneCanCreateInvoice: false,
requiresRefundEmail: false,
checkoutType: 'V1',
receipt: null,
lightningAmountInSatoshi: false,
lightningPrivateRouteHints: false,
onChainWithLnInvoiceFallback: false,
redirectAutomatically: false,
showRecommendedFee: true,
recommendedFeeBlockTarget: 1,
defaultLang: 'en',
customLogo: 'https://test.com',
customCSS: 'auto: 100px;',
htmlTitle: 'the best store ever',
networkFeeMode: 'MultiplePaymentsOnly',
payJoinEnabled: false,
lazyPaymentMethods: false,
defaultPaymentMethod: 'BTC'
);
}
/** @group createStore */
public function testItCanCreateAStore(): void
{
$this->assertStoreGettersAreSet($this->store);
}
/** @group getStores */
public function testItCanGetAllStores(): void
{
$stores = $this->storeClient->getStores();
$this->assertInstanceOf(StoreList::class, $stores);
foreach ($stores->all() as $store) {
$this->assertStoreGettersAreSet($store);
}
}
/** @group getStore */
public function testItCanGetAnIndividualStore(): void
{
$store = $this->storeClient->getStore($this->store->getId());
$this->assertStoreGettersAreSet($store);
}
private function assertStoreGettersAreSet(ResultStore $store): void
{
$this->assertInstanceOf(ResultStore::class, $store);
$this->assertIsString($store->getName());
$this->assertIsInt($store->getInvoiceExpiration());
$this->assertIsInt($store->getMonitoringExpiration());
$this->assertIsString($store->getSpeedPolicy());
$this->assertIsString($store->getLightningDescriptionTemplate());
$this->assertInstanceOf(PreciseNumber::class, $store->getPaymentTolerance());
$this->assertIsBool($store->anyoneCanCreateInvoice());
$this->assertIsBool($store->requiresRefundEmail());
$this->assertIsBool($store->lightningAmountInSatoshi());
$this->assertIsBool($store->lightningPrivateRouteHints());
$this->assertIsBool($store->onChainWithLnInvoiceFallback());
$this->assertIsBool($store->redirectAutomatically());
$this->assertIsBool($store->showRecommendedFee());
$this->assertIsInt($store->getRecommendedFeeBlockTarget());
if ($store->getCustomLogo() !== null) {
$this->assertIsString($store->getCustomLogo());
}
if ($store->getCustomCSS() !== null) {
$this->assertIsString($store->getCustomCSS());
}
if ($store->getHtmlTitle() !== null) {
$this->assertIsString($store->getHtmlTitle());
}
if ($store->getWebsite() !== null) {
$this->assertIsString($store->getWebsite());
}
if ($store->getDefaultPaymentMethod() !== null) {
$this->assertIsString($store->getDefaultPaymentMethod());
}
$this->assertIsString($store->getNetworkFeeMode());
$this->assertIsBool($store->payJoinEnabled());
$this->assertIsBool($store->lazyPaymentMethods());
$this->assertIsString($store->getId());
}
}