Skip to content

Commit

Permalink
First fully working version for OpenCart 3.
Browse files Browse the repository at this point in the history
  • Loading branch information
ndeet committed Oct 1, 2022
0 parents commit 22214dd
Show file tree
Hide file tree
Showing 124 changed files with 8,037 additions and 0 deletions.
19 changes: 19 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"name": "btcpayserver/opencart",
"description": "BTCPay Server plugin for OpenCart 3",
"type": "opencart-extension",
"require": {
"btcpayserver/btcpayserver-greenfield-php": "^1.3"
},
"license": "MIT",
"authors": [
{
"name": "Andreas Tasch",
"email": "[email protected]"
}
],
"minimum-stability": "stable",
"config": {
"vendor-dir": "upload/system/library/btcpay"
}
}
72 changes: 72 additions & 0 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

225 changes: 225 additions & 0 deletions upload/admin/controller/extension/payment/btcpay.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
<?php

/** Credits: Opencart plugin structure and classes inspired by Coingate payment plugin. */

use BTCPayServer\Client\Store;
use BTCPayServer\Client\Webhook;

require_once DIR_SYSTEM . 'library/btcpay/autoload.php';
require_once DIR_SYSTEM . 'library/btcpay/version.php';


class ControllerExtensionPaymentBTCPay extends Controller {
private $error = [];

public function index() {
$this->load->language('extension/payment/btcpay');
$this->document->setTitle($this->language->get('heading_title'));

$this->load->model('setting/setting');
$this->load->model('localisation/order_status');
$this->load->model('localisation/geo_zone');

if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
$host = $this->request->post['payment_btcpay_url'];
$apiKey = $this->request->post['payment_btcpay_api_auth_token'];
$storeId = $this->request->post['payment_btcpay_btcpay_storeid'];

// On saving we create a webhook if there is none yet.
if ($this->webhookExists() === false) {
$whData = $this->webhookSetup($host, $apiKey, $storeId);
$this->request->post['payment_btcpay_webhook'] = $whData;
} else {
// Check if the user wants to delete an existing webhook.
if ($this->request->post['payment_btcpay_webhook_delete'] === '1') {
// Try to delete the webhook on the provided host.
$this->webhookDelete($host, $apiKey, $storeId);
$this->request->post['payment_btcpay_webhook'] = NULL;
$this->request->post['payment_btcpay_webhook_delete'] = NULL;
} else {
// Need to convert existing webhook values back to array for storage.
$whString = $this->request->post['payment_btcpay_webhook'];
$whString = str_replace(['ID: ', 'SECRET: ', 'URL: '], '', $whString);
$whArr = explode(' | ', $whString);
$whData = [
'id' => $whArr[0],
'secret' => $whArr[1],
'url' => $whArr[2]
];
$this->request->post['payment_btcpay_webhook'] = $whData;
}
}

$this->model_setting_setting->editSetting('payment_btcpay', $this->request->post);
$this->session->data['success'] = $this->language->get('notice_success');
$this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=payment', true));
}

$data['action'] = $this->url->link('extension/payment/btcpay', 'user_token=' . $this->session->data['user_token'], true);
$data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=payment', true);
$data['order_statuses'] = $this->model_localisation_order_status->getOrderStatuses();

$data['geo_zones'] = $this->model_localisation_geo_zone->getGeoZones();

if (isset($this->error['warning'])) {
$data['error_warning'] = $this->error['warning'];
} else {
$data['error_warning'] = '';
}

$data['breadcrumbs'] = [];
$data['breadcrumbs'][] = array(
'text' => $this->language->get('text_home'),
'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
);
$data['breadcrumbs'][] = array(
'text' => $this->language->get('text_extension'),
'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=payment', true)
);
$data['breadcrumbs'][] = array(
'text' => $this->language->get('heading_title'),
'href' => $this->url->link('extension/payment/btcpay', 'user_token=' . $this->session->data['user_token'], true)
);

$fields = [
'payment_btcpay_status',
'payment_btcpay_url',
'payment_btcpay_api_auth_token',
'payment_btcpay_btcpay_storeid',
'payment_btcpay_webhook',
'payment_btcpay_webhook_delete',
'payment_btcpay_new_status_id',
'payment_btcpay_paid_status_id',
'payment_btcpay_settled_status_id',
'payment_btcpay_settled_paidover_status_id',
'payment_btcpay_invalid_status_id',
'payment_btcpay_expired_status_id',
'payment_btcpay_expired_partialpayment_status_id',
'payment_btcpay_expired_paidlate_status_id',
'payment_btcpay_refunded_status_id',
'payment_btcpay_total',
'payment_btcpay_geo_zone_id',
'payment_btcpay_debug_mode',
];

// Process our fields to be sure they are displayed.
foreach ($fields as $field) {
if (isset($this->request->post[$field])) {
$data[$field] = $this->request->post[$field];
} else {
$data[$field] = $this->config->get($field);
}
}

$data['payment_btcpay_sort_order'] = isset($this->request->post['payment_btcpay_sort_order']) ?
$this->request->post['payment_btcpay_sort_order'] : $this->config->get('payment_btcpay_sort_order');

$data['header'] = $this->load->controller('common/header');
$data['column_left'] = $this->load->controller('common/column_left');
$data['footer'] = $this->load->controller('common/footer');

$this->response->setOutput($this->load->view('extension/payment/btcpay', $data));
}

protected function validate() {
if (!$this->user->hasPermission('modify', 'extension/payment/btcpay')) {
$this->error['warning'] = $this->language->get('error_permission');
}

if (!class_exists('BTCPayServer\Client\Health')) {
$this->error['warning'] = $this->language->get('error_composer');
}

if (!$this->error) {
$host = $this->request->post['payment_btcpay_url'];
$apiKey = $this->request->post['payment_btcpay_api_auth_token'];
$storeId = $this->request->post['payment_btcpay_btcpay_storeid'];

try {
$client = new Store($host, $apiKey);
$store = $client->getStore($storeId);
if (empty($store->getId())) {
$this->error['warning'] = $this->language->get('error_store_not_found');
}
} catch (\Throwable $e) {
$this->error['warning'] = $this->language->get('error_connect_to_btcpay');
}
}

return !$this->error;
}

public function install() {
$this->load->model('extension/payment/btcpay');
$this->model_extension_payment_btcpay->install();
}

public function uninstall() {
$this->load->model('extension/payment/btcpay');
$this->model_extension_payment_btcpay->uninstall();
}

private function webhookExists() {
// Check if the config is any value set at all.
$data = $this->config->get('payment_btcpay_webhook');
if (empty($data)) {
return false;
}

// todo: load webhook form BTCPay to check if the callback url domain is the same

return true;
}

private function webhookSetup($host, $apiKey, $storeId) {
$whEvents = [
'InvoiceReceivedPayment',
'InvoicePaymentSettled',
'InvoiceProcessing',
'InvoiceExpired',
'InvoiceSettled',
'InvoiceInvalid'
];

try {
$whClient = new Webhook( $host, $apiKey );
$webhook = $whClient->createWebhook(
$storeId,
$this->webhookCallbackUrl(),
$whEvents,
null
);

// Prepare data for settings storage.
$whData = [
'id' => $webhook->getData()['id'],
'secret' => $webhook->getData()['secret'],
'url' => $webhook->getData()['url']
];

return $whData;
} catch (\Throwable $e) {
$this->log->write($e->getMessage());
}

return NULL;
}

private function webhookDelete($host, $apiKey, $storeId) {
$data = $this->config->get('payment_btcpay_webhook');

$client = new \BTCPayServer\Client\Webhook($host, $apiKey);
try {
$client->deleteWebhook($storeId, $data['id']);
} catch (\Throwable $e) {
$this->log->write('Error deleting webhook: ' . $e->getMessage());
}
}

private function webhookCallbackUrl() {
$url = $this->url->link('extension/payment/btcpay/callback', '', true);
// As we are in admin controller context we need to strip out the /admin
// path to receive the carrect frontend callback url.
return str_replace('admin/', '', $url);
}
}
48 changes: 48 additions & 0 deletions upload/admin/language/en-gb/extension/payment/btcpay.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

require_once DIR_SYSTEM . 'library/btcpay/version.php';

$_['heading_title'] = 'BTCPay Server';
$_['text_edit'] = 'Edit BTCPay Settings';
$_['text_version_info'] = 'Debug info: OpenCart ' . VERSION . ' with BTCPay Extension ' . BTCPAY_OPENCART_EXTENSION_VERSION . ' on PHP ' . phpversion();
$_['text_support_info'] = 'For setup instructions follow our <a href="https://docs.btcpayserver.org/OpenCart" target="_blank" rel="noopener">setup guide</a>. If you run into any problems feel free to <a href="https://github.com/btcpayserver/opencart" target="_blank" rel="noopener">open an issue on Github</a> or come to our <a href="https://chat.btcpayserver.org/" target="_blank" rel="noopener">Mattermost chat</a>.';

$_['text_extension'] = 'Extensions';

$_['entry_status'] = 'Payment Method Enabled';
$_['entry_btcpay_url'] = 'BTCPay Server URL';
$_['entry_api_auth_token'] = 'BTCPay API Key';
$_['entry_btcpay_storeid'] = 'BTCPay Store ID';
$_['entry_webhook'] = 'Webhook Data';
$_['entry_webhook_secret'] = 'Webhook Secret';
$_['entry_webhook_delete'] = 'Delete Webhook';
$_['entry_total'] = 'Total';
$_['entry_geo_zone'] = 'Geo Zone';
$_['entry_sort_order'] = 'Sort Order';
$_['entry_new_status'] = 'New Status';
$_['entry_paid_status'] = 'Paid (unconfirmed) Status';
$_['entry_settled_status'] = 'Settled Status';
$_['entry_settled_paidover_status'] = 'Settled (paid over)';
$_['entry_invalid_status'] = 'Invalid Status';
$_['entry_expired_status'] = 'Expired Status';
$_['entry_expired_partialpayment_status'] = 'Expired (partial payment) Status';
$_['entry_expired_paidlate_status'] = 'Expired (paid late) Status';
$_['entry_refunded_status'] = 'Refunded Status (not supported yet)';
$_['entry_sort_order'] = 'Sort Order';
$_['entry_debug_mode'] = 'Debug mode';

$_['help_btcpay_url'] = 'The public URL of your BTCPay Server instance. e.g. https://demo.mainnet.btcpayserver.org. You need to have a BTCPay Server instance running, see "Requirements" for several options of deployment on our <a href="https://docs.btcpayserver.org/OpenCart" target="_blank" rel="noopener">setup guide</a>.';
$_['help_webhook'] = 'The webhook will get created automatically after you entered BTCPay Server URL, API Key and Store ID. If you see this field filled with data (after you saved the form) all went well.';
$_['help_webhook_delete'] = 'This is useful if you switch hosts or have problems with webhooks. When checked this will delete the webhook on OpenCart (and BTCPay Server if possible). Make sure to delete the webhook on BTCPay Server Store settings too if not done automatically. <strong>ATTENTION:</strong> You need to edit and <strong>save</strong> this settings page again so a new webhook gets created on BTCPay Server.';
$_['help_total'] = 'The checkout total the order must reach before this payment method becomes active.';
$_['help_debug_mode'] = 'If enabled debug output will be saved to the error logs found in System -> Maintenance -> Error logs. Should be disabled after debugging.';

$_['notice_success'] = 'BTCPay Server Payment details have been successfully updated.';
$_['notice_success_webhook_renew'] = 'BTCPay Server Payment details have been successfully updated and successfully deleted webhook data.';

$_['error_permission'] = 'Warning: You do not have permission to modify BTCPay Server!';
$_['error_composer'] = 'Unable to load btcpayserver-greenfield-php. Please download a compiled vendor folder or run composer.';
$_['error_store_not_found'] = 'Successfully connected to BTCPay Server but no store with that ID found. Make sure you entered the correct store ID on that corresponding BTCPay Server URL.';
$_['error_connect_to_btcpay'] = 'Error connecting to BTCPay Server instance. Make sure you provided the correct URL, API key.';

$_['text_btcpay'] = '<a href="https://btcpayserver.org/" target="_blank" rel="noopener"><img src="view/image/payment/btcpay.png" alt="BTCPay Server" title="BTCPay Server" style="border: 1px solid #EEEEEE;" /></a>';
Loading

0 comments on commit 22214dd

Please sign in to comment.