Added ability for registrars to make their own deposit via Stripe

This commit is contained in:
Pinga 2023-11-17 20:55:24 +02:00
parent 2236e77d18
commit b8345209eb
8 changed files with 231 additions and 32 deletions

View file

@ -26,20 +26,6 @@ class FinancialsController extends Controller
public function deposit(Request $request, Response $response)
{
if ($_SESSION["auth_roles"] != 0) {
if ($request->getMethod() === 'POST') {
// Retrieve POST data
$data = $request->getParsedBody();
$db = $this->container->get('db');
$balance = $db->selectRow('SELECT name, email, accountBalance, creditLimit FROM registrar WHERE id = ?',
[ $_SESSION["auth_registrar_id"] ]
);
echo "Payment here";
return view($response,'admin/financials/deposit-registrar.twig', [
'balance' => $balance
]);
}
$db = $this->container->get('db');
$balance = $db->selectRow('SELECT name, accountBalance, creditLimit FROM registrar WHERE id = ?',
[ $_SESSION["auth_registrar_id"] ]
@ -127,4 +113,142 @@ class FinancialsController extends Controller
'registrars' => $registrars
]);
}
public function createPayment(Request $request, Response $response)
{
$postData = $request->getParsedBody();
$amount = $postData['amount']; // Make sure to validate and sanitize this amount
// Set Stripe's secret key
\Stripe\Stripe::setApiKey(envi('STRIPE_SECRET_KEY'));
// Convert amount to cents (Stripe expects the amount in the smallest currency unit)
$amountInCents = $amount * 100;
// Create Stripe Checkout session
$checkout_session = \Stripe\Checkout\Session::create([
'payment_method_types' => ['card', 'paypal'],
'line_items' => [[
'price_data' => [
'currency' => $_SESSION['_currency'],
'product_data' => [
'name' => 'Registrar Balance Deposit',
],
'unit_amount' => $amountInCents,
],
'quantity' => 1,
]],
'mode' => 'payment',
'success_url' => envi('APP_URL').'/payment-success?session_id={CHECKOUT_SESSION_ID}',
'cancel_url' => envi('APP_URL').'/payment-cancel',
]);
// Return session ID to the frontend
$response->getBody()->write(json_encode(['id' => $checkout_session->id]));
return $response->withHeader('Content-Type', 'application/json');
}
public function success(Request $request, Response $response)
{
$session_id = $request->getQueryParams()['session_id'] ?? null;
$db = $this->container->get('db');
if ($session_id) {
\Stripe\Stripe::setApiKey(envi('STRIPE_SECRET_KEY'));
try {
$session = \Stripe\Checkout\Session::retrieve($session_id);
$amountPaid = $session->amount_total; // Amount paid, in cents
$amount = $amountPaid / 100;
$amountPaidFormatted = number_format($amount, 2, '.', '');
$paymentIntentId = $session->payment_intent;
$isPositiveNumberWithTwoDecimals = filter_var($amount, FILTER_VALIDATE_FLOAT) !== false && preg_match('/^\d+(\.\d{1,2})?$/', $amount);
if ($isPositiveNumberWithTwoDecimals) {
$db->beginTransaction();
try {
$currentDateTime = new \DateTime();
$date = $currentDateTime->format('Y-m-d H:i:s.v');
$db->insert(
'statement',
[
'registrar_id' => $_SESSION['auth_registrar_id'],
'date' => $date,
'command' => 'create',
'domain_name' => 'deposit',
'length_in_months' => 0,
'from' => $date,
'to' => $date,
'amount' => $amount
]
);
$db->insert(
'payment_history',
[
'registrar_id' => $_SESSION['auth_registrar_id'],
'date' => $date,
'description' => 'Registrar Balance Deposit via Stripe ('.$paymentIntentId.')',
'amount' => $amount
]
);
$db->exec(
'UPDATE registrar SET accountBalance = (accountBalance + ?) WHERE id = ?',
[
$amount,
$_SESSION['auth_registrar_id'],
]
);
$db->commit();
} catch (Exception $e) {
$db->rollBack();
$balance = $db->selectRow('SELECT name, accountBalance, creditLimit FROM registrar WHERE id = ?',
[ $_SESSION["auth_registrar_id"] ]
);
return view($response, 'admin/financials/deposit-registrar.twig', [
'error' => $e->getMessage(),
'balance' => $balance
]);
}
$balance = $db->selectRow('SELECT name, accountBalance, creditLimit FROM registrar WHERE id = ?',
[ $_SESSION["auth_registrar_id"] ]
);
return view($response, 'admin/financials/deposit-registrar.twig', [
'deposit' => $amount,
'balance' => $balance
]);
} else {
$balance = $db->selectRow('SELECT name, accountBalance, creditLimit FROM registrar WHERE id = ?',
[ $_SESSION["auth_registrar_id"] ]
);
return view($response, 'admin/financials/deposit-registrar.twig', [
'error' => 'Invalid entry: Deposit amount must be positive. Please enter a valid amount.',
'balance' => $balance
]);
}
} catch (\Exception $e) {
$balance = $db->selectRow('SELECT name, accountBalance, creditLimit FROM registrar WHERE id = ?',
[ $_SESSION["auth_registrar_id"] ]
);
return view($response, 'admin/financials/deposit-registrar.twig', [
'error' => 'We encountered an issue while processing your payment. Please check your payment details and try again.',
'balance' => $balance
]);
}
}
}
public function cancel(Request $request, Response $response)
{
return view($response,'admin/financials/cancel.twig');
}
}

View file

@ -1,8 +1,8 @@
{
"name": "pinga/pinga-panel",
"description": "Pinga Framework Boilerplate",
"description": "Namingo Registry Control Panel",
"type": "project",
"keywords": ["slim", "slim 4", "skeleton", "authentication", "template", "orm","pinga"],
"keywords": ["slim", "slim 4", "domain", "registry", "panel"],
"homepage": "https://github.com/getpinga/pinga-panel",
"license": "MIT",
"authors": [
@ -34,11 +34,12 @@
"mevdschee/php-crud-api": "^2.14",
"gettext/gettext": "^5.7",
"punic/punic": "^3.8",
"league/iso3166": "^4.3"
"league/iso3166": "^4.3",
"stripe/stripe-php": "^13.3"
},
"autoload": {
"psr-4": {
"App\\": "app/"
}
"psr-4": {
"App\\": "app/"
}
}
}

View file

@ -72,4 +72,7 @@ return [
'api_key' => $_ENV['MAIL_API_KEY'] ?? 'test-api-key',
'api_provider' => $_ENV['MAIL_API_PROVIDER'] ?? 'sendgrid',
],
'payment' => [
'stripe' => $_ENV['STRIPE_SECRET_KEY'] ?? 'stripe-secret-key',
],
];

View file

@ -1,6 +1,6 @@
APP_NAME='CP'
APP_ENV=public
APP_URL=http://localhost
APP_URL=https://cp.example.com
APP_DOMAIN=example.com
DB_DRIVER=mysql
@ -22,3 +22,5 @@ MAIL_FROM_ADDRESS='example@domain.com'
MAIL_FROM_NAME='Example'
MAIL_API_KEY='test-api-key'
MAIL_API_PROVIDER='sendgrid'
STRIPE_SECRET_KEY='stripe-secret-key'

View file

@ -0,0 +1,51 @@
{% extends "layouts/app.twig" %}
{% block title %}{{ __('Deposit Payment Unsuccessful') }}{% endblock %}
{% block content %}
<div class="page-wrapper">
<!-- Page header -->
<div class="page-header d-print-none">
<div class="container-xl">
<div class="row g-2 align-items-center">
<div class="col">
<!-- Page pre-title -->
<div class="page-pretitle">
{{ __('Overview') }}
</div>
<h2 class="page-title">
{{ __('Deposit Payment Unsuccessful') }}
</h2>
</div>
</div>
</div>
</div>
<!-- Page body -->
<div class="page-body">
<div class="container-xl">
<div class="col-12">
<div class="card bg-orange-lt">
<div class="card-body">
<p class="text-secondary">We've noticed that your deposit payment process was not completed. It appears that the payment was either cancelled or failed during the transaction. If this was an error, or if you have any questions, please don't hesitate to contact us. We're here to help ensure your transaction is smooth and secure.</p>
<p class="text-secondary">Ready to try again? When you're set to proceed with your deposit, simply return to the <a href="{{route('deposit')}}">Deposit Page</a> to initiate a new payment. We value your partnership and are committed to assisting you every step of the way.</p>
</div>
</div>
</div>
</div>
</div>
</div>
<footer class="footer footer-transparent d-print-none">
<div class="container-xl">
<div class="col-12 col-lg-auto mt-3 mt-lg-0">
<ul class="list-inline list-inline-dots mb-0">
<li class="list-inline-item">
Copyright &copy; 2023
<a href="https://namingo.org" target="_blank" class="link-secondary">Namingo</a>.
</li>
</ul>
</div>
</div>
</div>
</footer>
</div>
{% endblock %}

View file

@ -52,15 +52,7 @@
<div class="card">
<div class="card-body">
<form id="depositForm" action="/deposit" method="post">
{{ csrf.field | raw }}
<div class="mb-3">
<label for="paymentMethod" class="form-label">Payment Method</label>
<select class="form-select" id="paymentMethod" name="method" required>
<option selected disabled value="">Choose Method...</option>
<option value="1">Stripe</option>
</select>
</div>
{{ csrf.field | raw }}
<div class="deposit-info">
<h5>Current Balance for {{ balance.name }}</h5>
<p class="fs-4">${{ balance.accountBalance }}</p>
@ -79,7 +71,7 @@
<div class="card-footer">
<div class="row align-items-center">
<div class="col-auto">
<button type="submit" class="btn btn-primary">Continue to Payment</button>
<button type="submit" class="btn btn-primary"><svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-brand-stripe" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M11.453 8.056c0 -.623 .518 -.979 1.442 -.979c1.69 0 3.41 .343 4.605 .923l.5 -4c-.948 -.449 -2.82 -1 -5.5 -1c-1.895 0 -3.373 .087 -4.5 1c-1.172 .956 -2 2.33 -2 4c0 3.03 1.958 4.906 5 6c1.961 .69 3 .743 3 1.5c0 .735 -.851 1.5 -2 1.5c-1.423 0 -3.963 -.609 -5.5 -1.5l-.5 4c1.321 .734 3.474 1.5 6 1.5c2 0 3.957 -.468 5.084 -1.36c1.263 -.979 1.916 -2.268 1.916 -4.14c0 -3.096 -1.915 -4.547 -5 -5.637c-1.646 -.605 -2.544 -1.07 -2.544 -1.807z" /></svg> Deposit with Stripe</button>
</div>
</div>
</div>
@ -103,4 +95,27 @@
</div>
</footer>
</div>
<script src="https://js.stripe.com/v3/"></script>
<script type="text/javascript">
var stripe = Stripe('YOUR_PUBLISHABLE_KEY'); // Replace with your publishable key
document.getElementById('depositForm').addEventListener('submit', function (e) {
e.preventDefault();
var formData = new FormData(this);
fetch('/create-payment', {
method: 'POST',
body: formData
})
.then(function (response) {
return response.json();
})
.then(function (session) {
return stripe.redirectToCheckout({ sessionId: session.id });
})
.catch(function (error) {
console.error('Error:', error);
});
});
</script>
{% endblock %}

View file

@ -165,7 +165,7 @@
</a>
</div>
</li>
<li {{ is_current_url('deposit') or is_current_url('transactions') or is_current_url('overview') or is_current_url('invoices') ? 'class="nav-item dropdown active"' : 'class="nav-item dropdown"' }}>
<li {{ is_current_url('deposit') or is_current_url('transactions') or is_current_url('overview') or is_current_url('invoices') or is_current_url('success') ? 'class="nav-item dropdown active"' : 'class="nav-item dropdown"' }}>
<a class="nav-link dropdown-toggle" href="#" data-bs-toggle="dropdown" data-bs-auto-close="outside" role="button" aria-expanded="false">
<span class="nav-link-icon d-md-none d-lg-inline-block"><svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M7 9m0 2a2 2 0 0 1 2 -2h10a2 2 0 0 1 2 2v6a2 2 0 0 1 -2 2h-10a2 2 0 0 1 -2 -2z"></path><path d="M14 14m-2 0a2 2 0 1 0 4 0a2 2 0 1 0 -4 0"></path><path d="M17 9v-2a2 2 0 0 0 -2 -2h-10a2 2 0 0 0 -2 2v6a2 2 0 0 0 2 2h2"></path></svg>
</span>

View file

@ -69,6 +69,9 @@ $app->group('', function ($route) {
$route->get('/invoices', FinancialsController::class .':invoices')->setName('invoices');
$route->map(['GET', 'POST'], '/deposit', FinancialsController::class .':deposit')->setName('deposit');
$route->map(['GET', 'POST'], '/create-payment', FinancialsController::class .':createPayment')->setName('createPayment');
$route->map(['GET', 'POST'], '/payment-success', FinancialsController::class .':success')->setName('success');
$route->map(['GET', 'POST'], '/payment-cancel', FinancialsController::class .':cancel')->setName('cancel');
$route->get('/transactions', FinancialsController::class .':transactions')->setName('transactions');
$route->get('/overview', FinancialsController::class .':overview')->setName('overview');