mirror of
https://github.com/v2board/v2board.git
synced 2025-03-12 21:54:42 +08:00
add cryptomus payment
This commit is contained in:
parent
0ca47622a5
commit
d8b64bb327
158
app/Payments/Cryptomus.php
Normal file
158
app/Payments/Cryptomus.php
Normal file
@ -0,0 +1,158 @@
|
||||
<?php
|
||||
namespace App\Payments;
|
||||
|
||||
use Library\Cryptomus\Payment as CryptomusPayment;
|
||||
|
||||
/**
|
||||
* Cryptomus crypto payment gateway
|
||||
*/
|
||||
class Cryptomus {
|
||||
|
||||
/**
|
||||
* @param $config
|
||||
*/
|
||||
public function __construct($config) {
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array[]
|
||||
*/
|
||||
public function form()
|
||||
{
|
||||
return [
|
||||
'cryptomus_api_key' => [
|
||||
'label' => 'Api key',
|
||||
'description' => 'You can find the API key in the settings of your personal account.',
|
||||
'type' => 'input',
|
||||
],
|
||||
'cryptomus_uuid' => [
|
||||
'label' => 'UUID',
|
||||
'description' => 'You can find the Merchant UUID in the settings of your personal account.',
|
||||
'type' => 'input',
|
||||
],
|
||||
'cryptomus_subtract' => [
|
||||
'label' => 'Substract',
|
||||
'description' => 'How much commission does the client pay (0-100%)',
|
||||
'type' => 'input',
|
||||
],
|
||||
'cryptomus_lifetime' => [
|
||||
'label' => 'Lifetime',
|
||||
'description' => 'The lifespan of the issued invoice.(In seconds)',
|
||||
'type' => 'input',
|
||||
],
|
||||
'cryptomus_currency' => [
|
||||
'label' => 'Fiat Currency',
|
||||
'description' => 'Default CNY',
|
||||
'type' => 'input'
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $order
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function pay($order) {
|
||||
$config = $this->config;
|
||||
|
||||
$paymentData = [
|
||||
'amount' => sprintf('%.2f', $order['total_amount'] / 100),
|
||||
'currency' => $config['cryptomus_currency'] ?? 'CNY',
|
||||
'order_id' => 'v2board_' . $order['trade_no'],
|
||||
'url_return' => $order['return_url'],
|
||||
'url_callback' => $order['notify_url'],
|
||||
'lifetime' => $config['cryptomus_lifetime'] ?? '3600',
|
||||
'subtract' => $config['cryptomus_subtract'] ?? '0',
|
||||
'plugin_name' => 'v2board:1.7.4',
|
||||
];
|
||||
|
||||
$paymentInstance = $this->getPayment();
|
||||
|
||||
try {
|
||||
$payment = $paymentInstance->create($paymentData);
|
||||
} catch (\Exception $exception) {
|
||||
info($exception->getMessage());
|
||||
abort(500, __($exception->getMessage()));
|
||||
}
|
||||
|
||||
return [
|
||||
'type' => 1, // Redirect to url
|
||||
'data' => $payment['url']
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return CryptomusPayment
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function getPayment()
|
||||
{
|
||||
$merchantUuid = trim($this->config['cryptomus_uuid']);
|
||||
$paymentKey = trim($this->config['cryptomus_api_key']);
|
||||
|
||||
if (!$merchantUuid && !$paymentKey) {
|
||||
info(__("Please fill UUID and API key"));
|
||||
abort(500, __("Please fill UUID and API key"));
|
||||
}
|
||||
|
||||
return new CryptomusPayment($paymentKey, $merchantUuid);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $params
|
||||
* @return array|false
|
||||
*/
|
||||
public function notify($params) {
|
||||
|
||||
$payload = trim(file_get_contents('php://input'));
|
||||
$data = json_decode($payload, true);
|
||||
|
||||
if (!$this->hashEqual($data)) {
|
||||
abort(400, 'Signature does not match');
|
||||
}
|
||||
|
||||
$success = !empty($data['is_final']) && ($data['status'] === 'paid' || $data['status'] === 'paid_over' || $data['status'] === 'wrong_amount');
|
||||
if ($success) {
|
||||
$orderId = preg_replace('/^v2board(?:_upd)?_/', '', $data['order_id'] ?? '');
|
||||
return [
|
||||
'trade_no' => $orderId,
|
||||
'callback_no' => $data['uuid']
|
||||
];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $data
|
||||
* @return bool
|
||||
*/
|
||||
private function hashEqual($data)
|
||||
{
|
||||
$paymentKey = trim($this->config['cryptomus_api_key']);
|
||||
|
||||
if (!$paymentKey) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$signature = $data['sign'];
|
||||
if (!$signature) {
|
||||
return false;
|
||||
}
|
||||
|
||||
unset($data['sign']);
|
||||
|
||||
$hash = md5(base64_encode(json_encode($data, JSON_UNESCAPED_UNICODE)) . $paymentKey);
|
||||
if (!hash_equals($hash, $signature)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
119
library/Cryptomus/Payment.php
Normal file
119
library/Cryptomus/Payment.php
Normal file
@ -0,0 +1,119 @@
|
||||
<?php
|
||||
namespace Library\Cryptomus;
|
||||
|
||||
use Library\Cryptomus\RequestBuilder;
|
||||
use Library\Cryptomus\RequestBuilderException;
|
||||
|
||||
final class Payment
|
||||
{
|
||||
/**
|
||||
* @var RequestBuilder
|
||||
*/
|
||||
private $requestBuilder;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $version = 'v1';
|
||||
|
||||
/**
|
||||
* @param string $paymentKey
|
||||
* @param string $merchantUuid
|
||||
*/
|
||||
public function __construct($paymentKey, $merchantUuid)
|
||||
{
|
||||
$this->requestBuilder = new RequestBuilder($paymentKey, $merchantUuid);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param array $parameters Additional parameters
|
||||
* @return bool|mixed
|
||||
* @throws RequestBuilderException
|
||||
*/
|
||||
public function services(array $parameters = [])
|
||||
{
|
||||
return $this->requestBuilder->sendRequest($this->version . '/payment/services', $parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
* - @var string amount: Amount to pay
|
||||
* - @var string currency: Payment currency
|
||||
* - @var string network: Payment network
|
||||
* - @var string order_id: Order ID in your system
|
||||
* - @var string url_return: Redirect link
|
||||
* - @var string url_callback: Callback link
|
||||
* - @var boolean is_payment_multiple: Allow surcharges on payment *
|
||||
* - @var string lifetime: Payment lifetime in seconds
|
||||
* - @var string to_currency: Currency to convert amount to
|
||||
* @return bool|mixed
|
||||
* @throws RequestBuilderException
|
||||
*/
|
||||
public function create(array $data)
|
||||
{
|
||||
return $this->requestBuilder->sendRequest($this->version . '/payment', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* uuid or order_id
|
||||
* @param array $data
|
||||
* - @var string uuid
|
||||
* - @var string order_id
|
||||
* @return bool|mixed
|
||||
* @throws RequestBuilderException
|
||||
*/
|
||||
public function info($data = [])
|
||||
{
|
||||
return $this->requestBuilder->sendRequest($this->version . '/payment/info', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|int $page Pagination cursor
|
||||
* @param array $parameters Additional parameters
|
||||
* @return bool|mixed
|
||||
* @throws RequestBuilderException
|
||||
*/
|
||||
public function history($page = 1, array $parameters = [])
|
||||
{
|
||||
$data = array_merge($parameters, ['cursor' => strval($page)]);
|
||||
return $this->requestBuilder->sendRequest($this->version . '/payment/list', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $parameters Additional parameters
|
||||
* @return bool|mixed
|
||||
* @throws RequestBuilderException
|
||||
*/
|
||||
public function balance(array $parameters = [])
|
||||
{
|
||||
return $this->requestBuilder->sendRequest($this->version . '/balance', $parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* uuid or order_id
|
||||
* @param array $data
|
||||
* - @var string uuid: Payment's UUID
|
||||
* - @var string order_id: Order ID in your system
|
||||
* @return bool|mixed
|
||||
* @throws RequestBuilderException
|
||||
*/
|
||||
public function reSendNotifications(array $data)
|
||||
{
|
||||
return $this->requestBuilder->sendRequest($this->version . '/payment/resend', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
* - @var string network: Network
|
||||
* - @var string currency: Payment currency
|
||||
* - @var string order_id: Order ID in your system
|
||||
* - @var string url_callback: Callback url
|
||||
* @return bool|mixed
|
||||
* @throws RequestBuilderException
|
||||
*/
|
||||
public function createWallet(array $data)
|
||||
{
|
||||
return $this->requestBuilder->sendRequest($this->version . '/wallet', $data);
|
||||
}
|
||||
}
|
90
library/Cryptomus/RequestBuilder.php
Normal file
90
library/Cryptomus/RequestBuilder.php
Normal file
@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
namespace Library\Cryptomus;
|
||||
|
||||
final class RequestBuilder
|
||||
{
|
||||
const API_URL = "https://api.cryptomus.com/";
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $secretKey;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $merchantUuid;
|
||||
|
||||
|
||||
/**
|
||||
* @param string $secretKey
|
||||
* @param string $merchantUuid
|
||||
*/
|
||||
public function __construct($secretKey, $merchantUuid)
|
||||
{
|
||||
$this->secretKey = $secretKey;
|
||||
$this->merchantUuid = $merchantUuid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $uri
|
||||
* @param array $data
|
||||
* @return bool|mixed
|
||||
* @throws RequestBuilderException
|
||||
*/
|
||||
public function sendRequest($uri, array $data = [])
|
||||
{
|
||||
$curl = curl_init();
|
||||
$url = self::API_URL . $uri;
|
||||
$body = json_encode($data, JSON_UNESCAPED_UNICODE);
|
||||
|
||||
$headers = [
|
||||
'Accept: application/json',
|
||||
'Content-Type: application/json;charset=UTF-8',
|
||||
'Content-Length: ' . strlen($body),
|
||||
'merchant: ' . $this->merchantUuid,
|
||||
'sign: ' . md5(base64_encode($body) . $this->secretKey)
|
||||
];
|
||||
|
||||
curl_setopt_array(
|
||||
$curl,
|
||||
[
|
||||
CURLOPT_URL => $url,
|
||||
CURLOPT_HTTPHEADER => $headers,
|
||||
CURLOPT_POST => 1,
|
||||
CURLOPT_POSTFIELDS => $body,
|
||||
CURLOPT_RETURNTRANSFER => 1,
|
||||
]
|
||||
);
|
||||
|
||||
$response = curl_exec($curl);
|
||||
$responseCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
||||
|
||||
if ($response === false) {
|
||||
throw new RequestBuilderException(curl_error($curl), $responseCode, $uri);
|
||||
}
|
||||
|
||||
if (false === empty($response)) {
|
||||
$json = json_decode($response, true);
|
||||
if (is_null($json)) {
|
||||
throw new RequestBuilderException(json_last_error_msg(), $responseCode, $uri);
|
||||
}
|
||||
|
||||
if ($responseCode !== 200 || (!is_null($json['state']) && $json['state'] != 0)) {
|
||||
if (!empty($json['message'])) {
|
||||
throw new RequestBuilderException($json['message'], $responseCode, $uri);
|
||||
}
|
||||
|
||||
if (!empty($json['errors'])) {
|
||||
throw new RequestBuilderException('Validation error', $responseCode, $uri, $json['errors']);
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($json['result']) && !is_null($json['state']) && $json['state'] == 0) {
|
||||
return $json['result'];
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
45
library/Cryptomus/RequestBuilderException.php
Normal file
45
library/Cryptomus/RequestBuilderException.php
Normal file
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
namespace Library\Cryptomus;
|
||||
|
||||
final class RequestBuilderException extends \Exception
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $method;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $errors;
|
||||
|
||||
/**
|
||||
* @param string $message
|
||||
* @param int $responseCode
|
||||
* @param string $uri
|
||||
* @param null|mixed $previous
|
||||
*/
|
||||
public function __construct($message, $responseCode, $uri, $errors = [], $previous = null)
|
||||
{
|
||||
$this->method = $uri;
|
||||
$this->errors = $errors;
|
||||
|
||||
parent::__construct($message, $responseCode, $previous);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getMethod()
|
||||
{
|
||||
return $this->method;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getErrors()
|
||||
{
|
||||
return $this->errors;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user