Compare commits

...

7 Commits

Author SHA1 Message Date
021-projects
c463d13c0b chore: bump version 2024-09-16 20:07:44 +03:00
021-projects
7f4821b4da refactor(purchase helper): update code for xf2.2 2024-09-16 19:56:37 +03:00
021-projects
fa8d607ad1 refactor(provider): update user alert repo class 2024-09-16 19:49:41 +03:00
021-projects
de5f078d9d chore: update deps 2024-09-16 19:45:46 +03:00
021-projects
081226e036 feat: add explanations for payment profile fields 2024-09-16 19:45:45 +03:00
021-projects
b9ad20e3b5 refactor(helpers): extract helpers to separate classes because of bad support of psr-4 in XenForo 2024-09-16 19:45:45 +03:00
021-projects
ca90eb5f56 feat: invoice alert 2024-09-16 19:45:45 +03:00
19 changed files with 167 additions and 29 deletions

View File

@ -0,0 +1,11 @@
<?php
namespace BS\BtcPayProvider\Helpers;
class Data
{
public static function get(array $arr, mixed $key, mixed $default = null): mixed
{
return $arr[$key] ?? $default;
}
}

View File

@ -0,0 +1,28 @@
<?php
namespace BS\BtcPayProvider\Helpers;
class Purchase
{
public static function purchaseToArray(\XF\Purchasable\Purchase $purchase): array
{
return [
'title' => $purchase->title,
'description' => $purchase->description,
'cost' => $purchase->cost,
'currency' => $purchase->currency,
'recurring' => $purchase->recurring,
'lengthAmount' => $purchase->lengthAmount,
'lengthUnit' => $purchase->lengthUnit,
'purchaser' => $purchase->purchaser,
'paymentProfile' => $purchase->paymentProfile,
'purchasableTypeId' => $purchase->purchasableTypeId,
'purchasableId' => $purchase->purchasableId,
'purchasableTitle' => $purchase->purchasableTitle,
'extraData' => $purchase->extraData,
'cancelUrl' => $purchase->cancelUrl,
'returnUrl' => $purchase->returnUrl,
'requestKey' => $purchase->requestKey,
];
}
}

View File

@ -2,6 +2,7 @@
namespace BS\BtcPayProvider\Payment;
use BS\BtcPayProvider\Helpers\Purchase as PurchaseHelper;
use BTCPayServer\Client\Invoice;
use BTCPayServer\Client\Store;
use BTCPayServer\Client\Webhook;
@ -12,6 +13,7 @@ use XF\Mvc\Controller;
use XF\Payment\AbstractProvider;
use XF\Payment\CallbackState;
use XF\Purchasable\Purchase;
use BTCPayServer\Result\Invoice as InvoiceResult;
class BTCPayServer extends AbstractProvider
{
@ -37,6 +39,10 @@ class BTCPayServer extends AbstractProvider
$scriptUrl = $purchaseRequest->PaymentProfile->options['host'] . '/modal/btcpay.js';
if ($purchaseRequest->PaymentProfile->options['invoice_alert'] ?? false) {
$this->sendAlertWithInvoice($purchase, $invoice, $scriptUrl);
}
return $controller->view(
'BS\BtcPayServer:Initiate\BTCPayServer',
'btcpay_show_invoice',
@ -44,6 +50,30 @@ class BTCPayServer extends AbstractProvider
);
}
protected function sendAlertWithInvoice(
Purchase $purchase,
InvoiceResult $invoice,
$scriptUrl
): void {
$visitor = \XF::visitor();
/** @var \XF\Repository\UserAlert $alertRepo */
$alertRepo = \XF::repository('XF:UserAlert');
$alertRepo->alert(
\XF::visitor(),
0,
'',
'user',
$visitor->user_id,
'btcpayprovider_invoice_created',
[
'purchase' => PurchaseHelper::purchaseToArray($purchase),
'invoice' => $invoice->getData(),
'scriptUrl' => $scriptUrl,
]
);
}
protected function createInvoice(
PurchaseRequest $purchaseRequest,
Purchase $purchase,
@ -180,6 +210,7 @@ class BTCPayServer extends AbstractProvider
$apiKey = $options['api_key'] ?? '';
$storeId = $options['store_id'] ?? '';
$secret = $options['secret'] ?? '';
$invoiceAlert = (bool) ($options['invoice_alert'] ?? false);
$host = $options['host'] = rtrim($host, '/');

View File

@ -4,8 +4,7 @@ namespace BS\BtcPayProvider\Payment\Concerns;
use BTCPayServer\Client\Invoice;
use XF\Payment\CallbackState;
use function BS\BtcPayProvider\Helpers\data_get;
use BS\BtcPayProvider\Helpers\Data;
trait Webhook
{
@ -23,7 +22,7 @@ trait Webhook
return CallbackState::PAYMENT_RECEIVED;
case 'InvoicePaymentSettled':
if (! data_get($state->purchaseRequest->extra_data, 'invoiceExpired')) {
if (! Data::get($state->purchaseRequest->extra_data, 'invoiceExpired')) {
$state->logType = 'info';
$state->logMessage = 'Invoice (partial) payment settled.';
return null;
@ -40,7 +39,7 @@ trait Webhook
case 'InvoiceReceivedPayment':
$state->logType = 'info';
if (data_get($payload, 'afterExpiration')) {
if (Data::get($payload, 'afterExpiration')) {
$state->logMessage = 'Invoice (partial) payment incoming (unconfirmed) after invoice was already expired.';
} else {
$state->logMessage = 'Invoice (partial) payment incoming (unconfirmed). Waiting for settlement.';
@ -49,7 +48,7 @@ trait Webhook
case 'InvoiceProcessing':
$state->logType = 'info';
if (data_get($payload, 'overPaid')) {
if (Data::get($payload, 'overPaid')) {
$state->logMessage = 'Invoice payment received fully with overpayment, waiting for settlement.';
} else {
$state->logMessage = 'Invoice payment received fully, waiting for settlement.';
@ -58,7 +57,7 @@ trait Webhook
case 'InvoiceExpired':
$state->logType = 'info';
if (data_get($payload, 'partiallyPaid')) {
if (Data::get($payload, 'partiallyPaid')) {
$state->logMessage = 'Invoice expired but was paid partially, please check.';
} else {
$state->logMessage = 'Invoice expired. No action to take.';
@ -68,7 +67,7 @@ trait Webhook
case 'InvoiceInvalid':
$state->logType = 'info';
if (data_get($payload, 'manuallyMarked')) {
if (Data::get($payload, 'manuallyMarked')) {
$state->logMessage = 'Invoice manually marked invalid.';
} else {
$state->logMessage = 'Invoice became invalid.';

View File

@ -35,28 +35,70 @@
"version_string": "1.0.0",
"hash": "1a273059c96929ea1713eedb738fb7e9"
},
"btcpayprovider_the_invoice_for_x_has_been_created_to_pay_y.txt": {
"global_cache": false,
"version_id": 1000070,
"version_string": "1.0.0",
"hash": "5c987636fc5c6cff251c7737070ecc64"
},
"btcpayserver_api_key.txt": {
"global_cache": false,
"version_id": 1000070,
"version_string": "1.0.0",
"hash": "d876ff8da67c3731ae25d8335a4168b4"
},
"btcpayserver_api_key_explain.txt": {
"global_cache": false,
"version_id": 1000070,
"version_string": "1.0.0",
"hash": "fb75ea4eba2d8925006acdd694d991eb"
},
"btcpayserver_enable_invoice_alert.txt": {
"global_cache": false,
"version_id": 1000070,
"version_string": "1.0.0",
"hash": "097d6d8ac165391105035b3588316aa8"
},
"btcpayserver_enable_invoice_alert_explain.txt": {
"global_cache": false,
"version_id": 1000070,
"version_string": "1.0.0",
"hash": "db14b16b33d56cf1d19cedbfe72b1b9d"
},
"btcpayserver_host.txt": {
"global_cache": false,
"version_id": 1000070,
"version_string": "1.0.0",
"hash": "c2ca16d048ec66e04bca283eab048ec2"
},
"btcpayserver_host_explain.txt": {
"global_cache": false,
"version_id": 1000070,
"version_string": "1.0.0",
"hash": "f0945f3c97a181c618bc8c319f812b2c"
},
"btcpayserver_secret.txt": {
"global_cache": false,
"version_id": 1000070,
"version_string": "1.0.0",
"hash": "1e6947ac7fb3a9529a9726eb692c8cc5"
},
"btcpayserver_secret_explain.txt": {
"global_cache": false,
"version_id": 1000070,
"version_string": "1.0.0",
"hash": "ed70c2d61b2d8676b8853a5824e29e14"
},
"btcpayserver_store_id.txt": {
"global_cache": false,
"version_id": 1000070,
"version_string": "1.0.0",
"hash": "1feafb67c1da8183793afe1f61bce2f4"
},
"btcpayserver_store_id_explain.txt": {
"global_cache": false,
"version_id": 1000070,
"version_string": "1.0.0",
"hash": "95a8a7ca79c1df2a2e6f45f03c75f02b"
}
}

View File

@ -0,0 +1 @@
<a id="{uid}" data-xf-click>The invoice</a> for {cost} has been created to pay {purchasable}.

View File

@ -0,0 +1 @@
<a href="https://docs.btcpayserver.org/Xenforo/#create-api-key" target="_blank">Where to get an API key?</a>

View File

@ -0,0 +1 @@
Enable invoice alert

View File

@ -0,0 +1 @@
If this option is active, when creating an invoice, the user will receive an alert containing a link to pay. This option will help avoid creating multiple invoices for one purchase, and will also allow the user to track the invoice status after closing the payment overlay.

View File

@ -0,0 +1 @@
Enter the address to the BTCPay instance you want to use.<br>Example: https://mainnet.demo.btcpayserver.org

View File

@ -0,0 +1 @@
<a href="https://docs.btcpayserver.org/Xenforo/#setup-webhook" target="_blank">How to setup a webhook and get a secret?</a>

View File

@ -0,0 +1 @@
You can find it under [Store Settings] > General.

View File

@ -2,7 +2,12 @@
"admin/payment_profile_btcPayServer.html": {
"version_id": 1000070,
"version_string": "1.0.0",
"hash": "6d3d44e7b96e193d4fccee22087def2c"
"hash": "6611ea1ab92867b91568f712c1539657"
},
"public/alert_user_btcpayprovider_invoice_created.html": {
"version_id": 1000070,
"version_string": "1.0.0",
"hash": "a9165cf1fe261e94789a71e7b5f4d84a"
},
"public/btcpay_show_invoice.html": {
"version_id": 1000070,

View File

@ -1,11 +1,20 @@
<xf:textboxrow name="options[host]" value="{$profile.options.host}"
label="{{ phrase('btcpayserver_host') }}"/>
label="{{ phrase('btcpayserver_host') }}"
explain="{{ phrase('btcpayserver_host_explain') }}" />
<xf:textboxrow name="options[api_key]" value="{$profile.options.api_key}"
label="{{ phrase('btcpayserver_api_key') }}" />
label="{{ phrase('btcpayserver_api_key') }}"
explain="{{ phrase('btcpayserver_api_key_explain') }}" />
<xf:textboxrow name="options[secret]" value="{$profile.options.secret}"
label="{{ phrase('btcpayserver_secret') }}" type="password" />
label="{{ phrase('btcpayserver_secret') }}"
explain="{{ phrase('btcpayserver_secret_explain') }}"
type="password" />
<xf:textboxrow name="options[store_id]" value="{$profile.options.store_id}"
label="{{ phrase('btcpayserver_store_id') }}" />
label="{{ phrase('btcpayserver_store_id') }}"
explain="{{ phrase('btcpayserver_store_id_explain') }}" />
<xf:checkboxrow explain="{{ phrase('btcpayserver_enable_invoice_alert_explain') }}">
<xf:option label="{{ phrase('btcpayserver_enable_invoice_alert') }}" name="options[invoice_alert]" checked="{$profile.options.invoice_alert}" />
</xf:checkboxrow>

View File

@ -0,0 +1,13 @@
<xf:set var="$uid">{{ unique_id() }}</xf:set>
{{ phrase('btcpayprovider_the_invoice_for_x_has_been_created_to_pay_y', {
'cost': $extra.purchase.cost|currency($extra.purchase.currency),
'purchasable': $extra.purchase.title,
'uid': $uid
}) }}
<xf:js src="{$extra.scriptUrl}" />
<xf:js>
(() => {
const link = document.querySelector('#{$uid}')
link.addEventListener('click', () => window.btcpay.showInvoice('{$extra.invoice.id}'))
})()
</xf:js>

View File

@ -2,8 +2,8 @@
"legacy_addon_id": "",
"title": "[021] BTCPay Provider",
"description": "Payment provider for BTCPay Server.",
"version_id": 1000070,
"version_string": "1.0.0",
"version_id": 1010070,
"version_string": "1.1.0",
"dev": "021",
"dev_url": "https://devsell.io/",
"faq_url": "",
@ -12,4 +12,4 @@
"require": [],
"icon": "icon.png",
"composer_autoload": "vendor/composer"
}
}

View File

@ -8,16 +8,16 @@
"packages": [
{
"name": "btcpayserver/btcpayserver-greenfield-php",
"version": "v2.3.0",
"version": "v2.7.0",
"source": {
"type": "git",
"url": "https://github.com/btcpayserver/btcpayserver-greenfield-php.git",
"reference": "9a01503ce9f395bdbb9326fd380650c8faf449f6"
"reference": "5e2ba7e3f585fc8e6dc068e22a0efbfdacd9c992"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/btcpayserver/btcpayserver-greenfield-php/zipball/9a01503ce9f395bdbb9326fd380650c8faf449f6",
"reference": "9a01503ce9f395bdbb9326fd380650c8faf449f6",
"url": "https://api.github.com/repos/btcpayserver/btcpayserver-greenfield-php/zipball/5e2ba7e3f585fc8e6dc068e22a0efbfdacd9c992",
"reference": "5e2ba7e3f585fc8e6dc068e22a0efbfdacd9c992",
"shasum": ""
},
"require": {
@ -56,9 +56,9 @@
"description": "BTCPay Server Greenfield API PHP client library.",
"support": {
"issues": "https://github.com/btcpayserver/btcpayserver-greenfield-php/issues",
"source": "https://github.com/btcpayserver/btcpayserver-greenfield-php/tree/v2.3.0"
"source": "https://github.com/btcpayserver/btcpayserver-greenfield-php/tree/v2.7.0"
},
"time": "2023-03-28T10:08:01+00:00"
"time": "2024-09-13T14:54:13+00:00"
}
],
"packages-dev": [],

View File

@ -1,8 +0,0 @@
<?php
namespace BS\BtcPayProvider\Helpers;
function data_get(array $arr, mixed $key, mixed $default = null): mixed
{
return $arr[$key] ?? $default;
}