Skip to content

Commit

Permalink
Dev 3.x merge new prs (#111)
Browse files Browse the repository at this point in the history
* Get v3 up to date with latest PRs on v2
  • Loading branch information
utxo-one authored May 6, 2023
1 parent 4d957d9 commit b4475a5
Show file tree
Hide file tree
Showing 15 changed files with 336 additions and 22 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/code_style.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:

steps:
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@v3

- name: Run PHP CS Fixer
uses: docker://oskarstark/php-cs-fixer-ga
Expand Down
11 changes: 5 additions & 6 deletions .github/workflows/phpunit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,21 @@ env:
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 ]
on: [push, pull_request]

jobs:
phpunit:
runs-on: ubuntu-latest
strategy:
matrix:
php-versions: ['8.0', '8.1']
phpunit-versions: ['latest']
php-versions: ["8.0", "8.1"]
phpunit-versions: ["latest"]


steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
fetch-depth: '0'
fetch-depth: "0"

- name: Setup PHP, with composer and extensions
uses: shivammathur/setup-php@v2
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/psalm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ jobs:
strategy:
fail-fast: false
matrix:
php-versions: ['8.0']
php-versions: ["8.0"]
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
fetch-depth: '0'
fetch-depth: "0"

- name: Setup PHP, with composer and extensions
uses: shivammathur/setup-php@v2
Expand Down
21 changes: 18 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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 7.3 and up. End-of-life'd versions will not be actively supported.
- 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.
Expand All @@ -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';
Expand All @@ -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.
34 changes: 32 additions & 2 deletions examples/api_key.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
// Fill in with your BTCPay Server data.
$apiKey = '';
$host = ''; // e.g. https://your.btcpay-server.tld
$storeId = '';
$invoiceId = '';
$email = ''; // e.g. [email protected]
$password = '';

// Get information about store on BTCPay Server.
try {
Expand All @@ -17,3 +17,33 @@
} catch (\Throwable $e) {
echo "Error: " . $e->getMessage();
}

/*
print("\nCreate a new api key (needs server modify permission of used api).\n");
try {
$client = new Apikey($host, $apiKey);
var_dump($client->createApiKey('api generated', ['btcpay.store.canmodifystoresettings']));
} catch (\Throwable $e) {
echo "Error: " . $e->getMessage();
}
*/
print("\nCreate a new api key for different user. Needs unrestricted access\n");

try {
$client = new Apikey($host, $apiKey);
$uKey = $client->createApiKeyForUser($userEmail, 'api generated to be deleted', ['btcpay.store.canmodifystoresettings']);
var_dump($uKey);
} catch (\Throwable $e) {
echo "Error: " . $e->getMessage();
}


print("\nRevoke api key for different user.\n");

try {
$client = new Apikey($host, $apiKey);
$uKey = $client->revokeApiKeyForUser($userEmail, $uKey->getData()['apiKey']);
var_dump($uKey);
} catch (\Throwable $e) {
echo "Error: " . $e->getMessage();
}
8 changes: 8 additions & 0 deletions examples/basic_usage.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,11 @@
} catch (\Throwable $e) {
echo "Error: " . $e->getMessage();
}

// Create a new store.
try {
$client = new Store($host, $apiKey);
var_dump($client->createStore('my new store'));
} catch (\Throwable $e) {
echo "Error: " . $e->getMessage();
}
12 changes: 11 additions & 1 deletion examples/user_usage.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public function createUser()
}
}

public function deleteUser()
public function deleteUser(string $userId)
{
$userId = '';

Expand All @@ -60,6 +60,16 @@ public function deleteUser()
echo "Error: " . $e->getMessage();
}
}

public function setUserLock(string $userId, bool $toggle)
{
try {
$client = new User($this->host, $this->apiKey);
var_dump($client->setUserLock($userId, $toggle));
} catch (\Throwable $e) {
echo "Error: " . $e->getMessage();
}
}
}

$users = new Users();
Expand Down
117 changes: 117 additions & 0 deletions src/Client/ApiKey.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,121 @@ public function getCurrent(): ResultApiKey
throw $this->getExceptionByStatusCode($method, $url, $response);
}
}

/**
* Create a new API key for current user.
*
* @param string $label Visible label on API key overview
* @param array $permissions The permissions array can contain specific store id
* e.g. btcpay.server.canmanageusers:2KxSpc9V5zDWfUbvgYiZuAfka4wUhGF96F75Ao8y4zHP
*/
public function createApikey(?string $label = null, ?array $permissions = null): ResultApiKey
{
$url = $this->getApiUrl() . 'api-keys';
$headers = $this->getRequestHeaders();
$method = 'POST';

$body = json_encode(
[
'label' => $label,
'permissions' => $permissions
],
JSON_THROW_ON_ERROR
);

$response = $this->getHttpClient()->request($method, $url, $headers, $body);

if ($response->getStatus() === 200) {
return new ResultApiKey(json_decode($response->getBody(), true, 512, JSON_THROW_ON_ERROR));
} else {
throw $this->getExceptionByStatusCode($method, $url, $response);
}
}

/**
* Create a new API key for a user.
*
* @param string $userId Can be user id or email.
* @param string $label Visible label on API key overview
* @param array $permissions The permissions array can contain specific store id
* e.g. btcpay.server.canmanageusers:2KxSpc9V5zDWfUbvgYiZuAfka4wUhGF96F75Ao8y4zHP
*/
public function createApiKeyForUser(
string $idOrMail,
?string $label = null,
?array $permissions = null
): ResultApiKey {
$url = $this->getApiUrl() . 'users/' . urlencode($idOrMail) . '/api-keys';
$headers = $this->getRequestHeaders();
$method = 'POST';

$body = json_encode(
[
'label' => $label,
'permissions' => $permissions
],
JSON_THROW_ON_ERROR
);

$response = $this->getHttpClient()->request($method, $url, $headers, $body);

if ($response->getStatus() === 200) {
return new ResultApiKey(json_decode($response->getBody(), true, 512, JSON_THROW_ON_ERROR));
} else {
throw $this->getExceptionByStatusCode($method, $url, $response);
}
}


/**
* Revokes the current API key.
*/
public function revokeCurrentApiKey(): bool
{
$url = $this->getApiUrl() . 'api-keys/current';
$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);
}
}

/**
* Revokes an API key for current user.
*/
public function revokeApiKey(string $apiKey): bool
{
$url = $this->getApiUrl() . 'api-keys/' . urlencode($apiKey);
$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);
}
}


/**
* Revokes the API key of target user.
*/
public function revokeApiKeyForUser(string $idOrMail, string $apiKey): bool
{
$url = $this->getApiUrl() . 'users/' . urlencode($idOrMail) . '/api-keys/' . urlencode($apiKey) ;
$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);
}
}
}
8 changes: 6 additions & 2 deletions src/Client/Invoice.php
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,12 @@ public function markInvoiceStatus(string $storeId, string $invoiceId, string $ma
}
}

public function updateInvoice(string $storeId, string $invoiceId, array $metaData): ResultInvoice
{
public function updateInvoice(
string $storeId,
string $invoiceId,
?array $metaData = null
): ResultInvoice {

$url = $this->getApiUrl() . 'stores/' . urlencode(
$storeId
) . '/invoices/' . urlencode($invoiceId);
Expand Down
1 change: 1 addition & 0 deletions src/Client/InvoiceCheckoutOptions.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ public static function create(
$options->paymentTolerance = $paymentTolerance;
$options->redirectURL = $redirectURL;
$options->redirectAutomatically = $redirectAutomatically;
$options->requiresRefundEmail = $requiresRefundEmail;
$options->defaultLanguage = $defaultLanguage;
$options->requiresRefundEmail = $requiresRefundEmail;
return $options;
Expand Down
Loading

0 comments on commit b4475a5

Please sign in to comment.