Skip to content

Commit

Permalink
MVP
Browse files Browse the repository at this point in the history
  • Loading branch information
Stierlitz committed May 4, 2023
1 parent a37309d commit ab93967
Show file tree
Hide file tree
Showing 6 changed files with 461 additions and 0 deletions.
103 changes: 103 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# Проста бібліотека для [MonobankPay](https://api.monobank.ua/)
Документація по REST API [тут](https://api.monobank.ua/docs/acquiring.html)

Для ведення запитів вам знадобиться токен з особистого кабінету [https://fop.monobank.ua/](https://fop.monobank.ua/) або тестовий токен з [https://api.monobank.ua/](https://api.monobank.ua/)

Встановити бібліотеку:
```bash
composer require stierlitz/monobank-pay
```

### Мінімальні вимоги:
* php >=7.4
* guzzlehttp/guzzle >= 7.0
* starkbank/ecdsa >= 0.0.5

### Приклади використання:
```php
require_once('vendor/autoload.php');

//створили клієнта - через нього запити будуть слатись
$monoClient = new \MonoPay\Client('YOUR_TOKEN_HERE');

//із клієнта можна отримати id та назву мерчанта
echo $monoClient->getMerchantId();
echo $monoClient->getMerchantName();

//для створення платежів створюємо цей об'єкт
$monoPayment = new \MonoPay\Payment($monoClient);

//створення платежу
$invoice = $monoPayment->create(1000,[
'merchantPaymInfo' => [ //деталі оплати
'reference' => 'my_shop_order_28142', //Номер чека, замовлення, тощо; визначається мерчантом (вами)
'destination' => 'Оплата за замовлення #28142', //Призначення платежу
'basketOrder' => [ //Склад замовлення, використовується для відображення кошика замовлення
[
'name' => 'Товар1', //Назва товару
'qty' => 2, //Кількість
'sum' => 500, //Сума у мінімальних одиницях валюти за одиницю товару
'icon' => 'https://example.com/images/product1.jpg', //Посилання на зображення товару
'unit' => 'уп.', //Назва одиниці вимiру товару
]
]
],
'redirectUrl' => 'https://example.com/order-result', //Адреса для повернення (GET) - на цю адресу буде переадресовано користувача після завершення оплати (у разі успіху або помилки)
'webHookUrl' => 'https://example.com/mono-webhook', //Адреса для CallBack (POST) – на цю адресу буде надіслано дані про стан платежу при кожній зміні статусу. Зміст тіла запиту ідентичний відповіді запиту “перевірки статусу рахунку”
'validity' => 3600*24*7, //Строк дії в секундах, за замовчуванням рахунок перестає бути дійсним через 24 години
'paymentType' => 'debit', //debit | hold. Тип операції. Для значення hold термін складає 9 днів. Якщо через 9 днів холд не буде фіналізовано — він скасовується
]);
print_r($invoice);

//інформація про платіж
$invoice = $monoPayment->info('2305046jUBEj8WfyaBdB');
print_r($invoice);

//відшкодування
$result = $monoPayment->refund('2305046jUBEj8WfyaBdB');
print_r($result);

//скасування посилання на оплату
$result = $monoPayment->cancel('2305046jUBEj8WfyaBdB');
print_r($result);

//деталі успішної оплати
$invoiceDetails = $monoPayment->successDetails('2305046jUBEj8WfyaBdB');
print_r($invoiceDetails);

//списати заблоковану сумму
//зверніть увагу: списати можна тільки таку самму або меншу сумму яку ви заблокували
$result = $monoPayment->captureHold('2305046jUBEj8WfyaBdB',500);
print_r($result);

//список успішних оплат за останні сутки
$list = $monoPayment->items(time()-60*60*24);
print_r($list);
```

### Отримання вебхуку:
```php
require_once('vendor/autoload.php');

//створили клієнта - через нього запити будуть слатись
$monoClient = new \MonoPay\Client('YOUR_TOKEN_HERE');

//отримання публічного ключа (бажано закешувати)
$publicKey = $monoClient->getPublicKey();

//класс для роботи з вебхуком
$monoWebhook = new \MonoPay\Webhook($monoClient,$publicKey,$_SERVER['HTTP_X_SIGN']);
//отримуємо вхідні дані
$body = file_get_contents('php://input');
//валідуємо дані
if($monoWebhook->verify($body)){
echo "Ці дані прислав монобанк, можна обробляти";
}else{
echo "Дані прислав шахрай, ігноруємо";
}
```

#### TODO List:
* Доробити методи стосовно токенізації карт
* Переробити вхідні параметри і вихідні дані на класи з описаними методами
* Попросити в Гороховського баночку пива
23 changes: 23 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"name": "plakidan/monobank-pay",
"description": "This is simple library for Monobank Acquiring called \"MonoPay\" based on REST API (v2304) docs located at https://api.monobank.ua/docs/acquiring.html",
"type": "library",
"license": "Apache-2.0",
"authors": [
{
"name": "Stierlitz",
"email": "[email protected]"
}
],
"minimum-stability": "dev",
"require": {
"php": ">=7.4",
"guzzlehttp/guzzle": "^7.0",
"starkbank/ecdsa": "0.0.5"
},
"autoload": {
"psr-4": {
"MonoPay\\": "src/"
}
}
}
88 changes: 88 additions & 0 deletions src/Client.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<?php


namespace MonoPay;

class Client extends RequestBuilder
{
private ?string $merchantId;
private ?string $merchantName;
public string $apiEndpoint = 'https://api.monobank.ua/';
private \GuzzleHttp\Client $httpClient;

/**
* Створює клієнт з ключем для запитів до серверу Mono і отримує дані про мерчант
* @param string $token Токен з особистого кабінету https://fop.monobank.ua/ або тестовий токен з https://api.monobank.ua/
* @throws \GuzzleHttp\Exception\GuzzleException
* @link https://api.monobank.ua/docs/acquiring.html#/paths/~1api~1merchant~1details/get Так отримуються деталі мерчанту
*/
public function __construct(string $token)
{
$this->httpClient = new \GuzzleHttp\Client([
'base_uri' => $this->apiEndpoint,
\GuzzleHttp\RequestOptions::TIMEOUT => 5,
\GuzzleHttp\RequestOptions::HEADERS => [
'X-Token' => $token,
],
\GuzzleHttp\RequestOptions::HTTP_ERRORS => false
]);
$response = $this->httpClient->request('GET', '/api/merchant/details');
$json = $response->getBody()->getContents();
$data = json_decode($json, true);
if ($response->getStatusCode() == '200') {
if ($data && isset($data['merchantId']) && isset($data['merchantName'])) {
$this->merchantId = $data['merchantId'];
$this->merchantName = $data['merchantName'];
} else {
throw new \Exception('Cannot decode json response from Mono', 500);
}
} else {
throw new \Exception($data['errorDescription'] ?? 'Unknown error response: ' . $json, $response->getStatusCode());
}
}

public function getMerchantId(): string
{
return $this->merchantId;
}

public function getMerchantName(): string
{
return $this->merchantName;
}

public function getClient(): \GuzzleHttp\Client
{
return $this->httpClient;
}

/**
* Відкритий ключ для верифікації підписів
* Отримання відкритого ключа для перевірки підпису, який включено у вебхуки. Ключ можна кешувати і робити запит на отримання нового, коли верифікація підпису з поточним ключем перестане працювати. Кожного разу робити запит на отримання ключа не треба
* @link https://api.monobank.ua/docs/acquiring.html#/paths/~1api~1merchant~1pubkey/get
* @throws \GuzzleHttp\Exception\GuzzleException
* @throws \Exception
*/
public function getPublicKey(): string
{
$response = $this->getClient()->request('GET','/api/merchant/pubkey');
$data = $this->getDataFromGuzzleResponse($response);
if(!isset($data['key'])){
throw new \Exception('Invalid response from Mono API',500);
}
return $data['key'];
}

/**
* Дані мерчанта
* @link https://api.monobank.ua/docs/acquiring.html#/paths/~1api~1merchant~1details/get
* @return array Масив з ключами merchantId та merchantName
* @throws \GuzzleHttp\Exception\GuzzleException
* @throws \Exception
*/
public function getMerchant(): array
{
$response = $this->getClient()->request('GET','/api/merchant/details');
return $this->getDataFromGuzzleResponse($response);
}
}
164 changes: 164 additions & 0 deletions src/Payment.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
<?php


namespace MonoPay;


class Payment extends RequestBuilder
{
private \MonoPay\Client $client;

public function __construct(\MonoPay\Client $client)
{
$this->client = $client;
}

/**
* Створення рахунку
* Створення рахунку для оплати
* @param int $amount Сума оплати у мінімальних одиницях (копійки для гривні)
* @param array $options Додаткові параметри (Див. посилання)
* @return array
* @throws \GuzzleHttp\Exception\GuzzleException
* @link https://api.monobank.ua/docs/acquiring.html#/paths/~1api~1merchant~1invoice~1create/post
*/
public function create(int $amount, array $options=[]): array
{
if($amount < 1){
throw new \Exception('Amount must be a natural number',500);
}
$options['amount']=$amount;
$response = $this->client->getClient()->request('POST','/api/merchant/invoice/create',[
\GuzzleHttp\RequestOptions::JSON => $options
]);

return $this->getDataFromGuzzleResponse($response);
}

/**
* Статус рахунку
* Метод перевірки статусу рахунку при розсинхронізації з боку продавця або відсутності webHookUrl при створенні рахунку.
* @param string $invoiceId ID рахунку
* @link https://api.monobank.ua/docs/acquiring.html#/paths/~1api~1merchant~1invoice~1status?invoiceId=%7BinvoiceId%7D/get
* @throws \GuzzleHttp\Exception\GuzzleException
* @throws \Exception
*/
public function info(string $invoiceId): array
{
$response = $this->client->getClient()->request('GET','/api/merchant/invoice/status',[
\GuzzleHttp\RequestOptions::QUERY => [
'invoiceId' => $invoiceId
]
]);

return $this->getDataFromGuzzleResponse($response);
}

/**
* Скасування оплати
* Скасування успішної оплати рахунку
* @param string $invoiceId ID рахунку
* @param array $options Додаткові параметри (Див. посилання)
* @link https://api.monobank.ua/docs/acquiring.html#/paths/~1api~1merchant~1invoice~1cancel/post
* @throws \GuzzleHttp\Exception\GuzzleException
* @throws \Exception
*/
public function refund(string $invoiceId, array $options=[]): array
{
$options['invoiceId'] = $invoiceId;

$response = $this->client->getClient()->request('POST','/api/merchant/invoice/cancel',[
\GuzzleHttp\RequestOptions::JSON => $options
]);

return $this->getDataFromGuzzleResponse($response);
}

/**
* Інвалідація рахунку
* Інвалідація рахунку, якщо за ним ще не було здіснено оплати
* @param string $invoiceId ID рахунку
* @link https://api.monobank.ua/docs/acquiring.html#/paths/~1api~1merchant~1invoice~1remove/post
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function cancel(string $invoiceId): array
{
$response = $this->client->getClient()->request('POST','/api/merchant/invoice/remove',[
\GuzzleHttp\RequestOptions::JSON => [
'invoiceId' => $invoiceId
]
]);
return $this->getDataFromGuzzleResponse($response);
}

/**
* Розширена інформація про успішну оплату
* Дані про успішну оплату, якщо вона була здійснена
* @param string $invoiceId Ідентифікатор рахунку
* @throws \GuzzleHttp\Exception\GuzzleException
* @throws \Exception
*@link https://api.monobank.ua/docs/acquiring.html#/paths/~1api~1merchant~1invoice~1payment-info?invoiceId=%7BinvoiceId%7D/get
*/
public function successDetails(string $invoiceId): array
{
$response = $this->client->getClient()->request('GET','/api/merchant/invoice/payment-info',[
\GuzzleHttp\RequestOptions::QUERY => [
'invoiceId' => $invoiceId
]
]);

return $this->getDataFromGuzzleResponse($response);
}

/**
* Фіналізація суми холду
* Фінальна сумма списання має бути нижчою або дорівнювати суммі холду
* @param string $invoiceId Ідентифікатор рахунку
* @param int|null $amount Сума у мінімальних одиницях, якщо бажаєте змінити сумму списання
* @return array
* @throws \GuzzleHttp\Exception\GuzzleException
* @throws \Exception
* @link https://api.monobank.ua/docs/acquiring.html#/paths/~1api~1merchant~1invoice~1finalize/post
*/
public function captureHold(string $invoiceId, int $amount = null): array
{
$body = [
'invoiceId' => $invoiceId
];
if(isset($amount)){
$body['amount'] = $amount;
}
$response = $this->client->getClient()->request('POST','/api/merchant/invoice/finalize',[
\GuzzleHttp\RequestOptions::JSON => $body
]);

return $this->getDataFromGuzzleResponse($response);
}

/**
* Виписка за період
* Список платежів за вказаний період
* @param int $fromTimestamp UTC Unix timestamp
* @param int|null $toTimestamp UTC Unix timestamp
* @return array
* @throws \GuzzleHttp\Exception\GuzzleException
* @throws \Exception
* @link https://api.monobank.ua/docs/acquiring.html#/paths/~1api~1merchant~1statement/get
*/
public function items(int $fromTimestamp, int $toTimestamp=null): array
{
$query = [
'from' => $fromTimestamp
];
if(isset($toTimestamp)){
$query['to'] = $toTimestamp;
}
$response = $this->client->getClient()->request('GET','/api/merchant/statement',[
\GuzzleHttp\RequestOptions::QUERY => $query
]);

$data = $this->getDataFromGuzzleResponse($response);
return $data['list']??[];
}

}
Loading

0 comments on commit ab93967

Please sign in to comment.