Przelewy24
This commit is contained in:
parent
9be2881d54
commit
88f3bf5bcc
5
db/migrations/035_add_p24_to_orders.sql
Normal file
5
db/migrations/035_add_p24_to_orders.sql
Normal file
@ -0,0 +1,5 @@
|
||||
ALTER TABLE `orders`
|
||||
ADD COLUMN `payment_status` VARCHAR(255) DEFAULT 'pending',
|
||||
ADD COLUMN `p24_session_id` VARCHAR(255) NULL,
|
||||
ADD COLUMN `p24_order_id` VARCHAR(255) NULL,
|
||||
ADD COLUMN `paid_at` DATETIME NULL;
|
||||
@ -10787,3 +10787,52 @@ Product price query executed. Found: {"price_net":"233.20","price_gross":"286.84
|
||||
Found product price. Net: 233.2, Gross: 286.84
|
||||
FINAL: Returning Net: 233.2, Gross: 286.84
|
||||
---
|
||||
---
|
||||
START getEffectivePrice for product 1, client
|
||||
Client price not found or not set, falling back to product price.
|
||||
Product price query executed. Found: {"price_net":"1111.00","price_gross":"1366.53"}
|
||||
Found product price. Net: 1111, Gross: 1366.53
|
||||
FINAL: Returning Net: 1111, Gross: 1366.53
|
||||
---
|
||||
---
|
||||
START getEffectivePrice for product 2, client
|
||||
Client price not found or not set, falling back to product price.
|
||||
Product price query executed. Found: {"price_net":"1318.05","price_gross":"1621.20"}
|
||||
Found product price. Net: 1318.05, Gross: 1621.2
|
||||
FINAL: Returning Net: 1318.05, Gross: 1621.2
|
||||
---
|
||||
---
|
||||
START getEffectivePrice for product 3, client
|
||||
Client price not found or not set, falling back to product price.
|
||||
Product price query executed. Found: {"price_net":"9.95","price_gross":"12.24"}
|
||||
Found product price. Net: 9.95, Gross: 12.24
|
||||
FINAL: Returning Net: 9.95, Gross: 12.24
|
||||
---
|
||||
---
|
||||
START getEffectivePrice for product 4, client
|
||||
Client price not found or not set, falling back to product price.
|
||||
Product price query executed. Found: {"price_net":"9.95","price_gross":"12.24"}
|
||||
Found product price. Net: 9.95, Gross: 12.24
|
||||
FINAL: Returning Net: 9.95, Gross: 12.24
|
||||
---
|
||||
---
|
||||
START getEffectivePrice for product 5, client
|
||||
Client price not found or not set, falling back to product price.
|
||||
Product price query executed. Found: {"price_net":"68.00","price_gross":"83.64"}
|
||||
Found product price. Net: 68, Gross: 83.64
|
||||
FINAL: Returning Net: 68, Gross: 83.64
|
||||
---
|
||||
---
|
||||
START getEffectivePrice for product 6, client
|
||||
Client price not found or not set, falling back to product price.
|
||||
Product price query executed. Found: {"price_net":"171.60","price_gross":"211.07"}
|
||||
Found product price. Net: 171.6, Gross: 211.07
|
||||
FINAL: Returning Net: 171.6, Gross: 211.07
|
||||
---
|
||||
---
|
||||
START getEffectivePrice for product 7, client
|
||||
Client price not found or not set, falling back to product price.
|
||||
Product price query executed. Found: {"price_net":"233.20","price_gross":"286.84"}
|
||||
Found product price. Net: 233.2, Gross: 286.84
|
||||
FINAL: Returning Net: 233.2, Gross: 286.84
|
||||
---
|
||||
|
||||
116
includes/Przelewy24.php
Normal file
116
includes/Przelewy24.php
Normal file
@ -0,0 +1,116 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/p24_config.php';
|
||||
|
||||
class Przelewy24 {
|
||||
private $merchantId;
|
||||
private $posId;
|
||||
private $crc;
|
||||
private $apiKey;
|
||||
private $baseUrl;
|
||||
private $isSandbox;
|
||||
|
||||
public function __construct() {
|
||||
$this->merchantId = P24_MERCHANT_ID;
|
||||
$this->posId = P24_POS_ID;
|
||||
$this->crc = P24_CRC;
|
||||
$this->apiKey = P24_API_KEY;
|
||||
$this->baseUrl = P24_BASE_URL;
|
||||
$this->isSandbox = (P24_ENV === 'sandbox');
|
||||
}
|
||||
|
||||
public function createSign($json_data) {
|
||||
return hash('sha384', $json_data . $this->crc);
|
||||
}
|
||||
|
||||
public function registerTransaction(array $data) {
|
||||
$payload = [
|
||||
'merchantId' => $this->merchantId,
|
||||
'posId' => $this->posId,
|
||||
'sessionId' => $data['sessionId'],
|
||||
'amount' => $data['amount'],
|
||||
'currency' => 'PLN',
|
||||
'description' => $data['description'],
|
||||
'email' => $data['email'],
|
||||
'client' => $data['client'],
|
||||
'urlReturn' => P24_URL_RETURN,
|
||||
'urlStatus' => P24_URL_STATUS,
|
||||
'language' => 'pl',
|
||||
];
|
||||
$payload['sign'] = $this->createTransactionSign($payload);
|
||||
|
||||
return $this->postRequest('/api/v1/transaction/register', $payload);
|
||||
}
|
||||
|
||||
public function verifyTransaction(array $data) {
|
||||
$payload = [
|
||||
'merchantId' => $this->merchantId,
|
||||
'posId' => $this->posId,
|
||||
'sessionId' => $data['sessionId'],
|
||||
'amount' => $data['amount'],
|
||||
'currency' => 'PLN',
|
||||
'orderId' => $data['orderId'],
|
||||
];
|
||||
$payload['sign'] = $this->createVerificationSign($payload);
|
||||
|
||||
return $this->putRequest('/api/v1/transaction/verify', $payload);
|
||||
}
|
||||
|
||||
public function getRedirectUrl($token) {
|
||||
return $this->baseUrl . '/trnRequest/' . $token;
|
||||
}
|
||||
|
||||
private function createTransactionSign(array $payload) {
|
||||
$json = json_encode([
|
||||
"sessionId" => $payload['sessionId'],
|
||||
"merchantId" => $this->merchantId,
|
||||
"amount" => $payload['amount'],
|
||||
"currency" => $payload['currency'],
|
||||
"crc" => $this->crc,
|
||||
]);
|
||||
return hash('sha384', $json);
|
||||
}
|
||||
|
||||
private function createVerificationSign(array $payload) {
|
||||
$json = json_encode([
|
||||
"sessionId" => $payload['sessionId'],
|
||||
"orderId" => $payload['orderId'],
|
||||
"amount" => $payload['amount'],
|
||||
"currency" => $payload['currency'],
|
||||
"crc" => $this->crc,
|
||||
]);
|
||||
return hash('sha384', $json);
|
||||
}
|
||||
|
||||
private function postRequest($endpoint, $data) {
|
||||
return $this->sendRequest('POST', $endpoint, $data);
|
||||
}
|
||||
|
||||
private function putRequest($endpoint, $data) {
|
||||
return $this->sendRequest('PUT', $endpoint, $data);
|
||||
}
|
||||
|
||||
private function sendRequest($method, $endpoint, $data) {
|
||||
$url = $this->baseUrl . $endpoint;
|
||||
|
||||
$ch = curl_init($url);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
|
||||
curl_setopt($ch, CURLOPT_USERPWD, $this->posId . ':' . $this->apiKey);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
'Content-Type: application/json',
|
||||
]);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
if ($http_code >= 200 && $http_code < 300) {
|
||||
return json_decode($response, true);
|
||||
} else {
|
||||
// Handle error, log response for debugging
|
||||
error_log("P24 API Error: HTTP {$http_code} - {$response}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
27
includes/p24_config.php
Normal file
27
includes/p24_config.php
Normal file
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
// Przelewy24 Configuration
|
||||
// IMPORTANT: Please fill in your details below. These are placeholder values.
|
||||
|
||||
// Environment: 'sandbox' or 'production'
|
||||
define('P24_ENV', getenv('P24_ENV') ?: 'sandbox');
|
||||
|
||||
// Merchant Credentials from P24 Panel
|
||||
define('P24_MERCHANT_ID', getenv('P24_MERCHANT_ID') ?: 0000);
|
||||
define('P24_POS_ID', getenv('P24_POS_ID') ?: 0000);
|
||||
define('P24_CRC', getenv('P24_CRC') ?: 'YOUR_CRC_KEY');
|
||||
define('P24_API_KEY', getenv('P24_API_KEY') ?: 'YOUR_API_KEY');
|
||||
|
||||
// P24 API URLs
|
||||
$p24_base_urls = [
|
||||
'sandbox' => 'https://sandbox.przelewy24.pl',
|
||||
'production' => 'https://secure.przelewy24.pl'
|
||||
];
|
||||
define('P24_BASE_URL', $p24_base_urls[P24_ENV]);
|
||||
|
||||
// Your Application URLs
|
||||
// These should be full URLs accessible by P24 servers.
|
||||
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] == 443) ? "https://" : "http://";
|
||||
$host = $_SERVER['HTTP_HOST'];
|
||||
|
||||
define('P24_URL_RETURN', getenv('P24_URL_RETURN') ?: $protocol . $host . '/p24_return.php');
|
||||
define('P24_URL_STATUS', getenv('P24_URL_STATUS') ?: $protocol . $host . '/p24_status.php');
|
||||
@ -109,11 +109,55 @@ try {
|
||||
]);
|
||||
}
|
||||
|
||||
$pdo->commit();
|
||||
|
||||
// Handle Przelewy24 payment
|
||||
if ($_POST['payment_method'] === 'przelewy24') {
|
||||
require_once 'includes/Przelewy24.php';
|
||||
$p24 = new Przelewy24();
|
||||
|
||||
// Unique session ID for this payment attempt
|
||||
$p24_session_id = uniqid('order_' . $order_id . '_', true);
|
||||
|
||||
// Update order with the session ID
|
||||
$stmt = $pdo->prepare('UPDATE orders SET p24_session_id = ? WHERE id = ?');
|
||||
$stmt->execute([$p24_session_id, $order_id]);
|
||||
|
||||
$user_id = $_SESSION['user_id'] ?? null;
|
||||
$stmt = $pdo->prepare('SELECT email, name FROM users WHERE id = ?');
|
||||
$stmt->execute([$user_id]);
|
||||
$user = $stmt->fetch();
|
||||
$client_email = $user['email'] ?? '';
|
||||
$client_name = $user['name'] ?? 'Customer';
|
||||
|
||||
// Register transaction with P24
|
||||
$p24_data = [
|
||||
'sessionId' => $p24_session_id,
|
||||
'amount' => (int)($total_amount_gross * 100), // Amount in grosze
|
||||
'description' => "Order #" . $order_id,
|
||||
'email' => $client_email,
|
||||
'client' => $client_name,
|
||||
];
|
||||
|
||||
$response = $p24->registerTransaction($p24_data);
|
||||
|
||||
if (isset($response['data']['token'])) {
|
||||
$token = $response['data']['token'];
|
||||
$redirect_url = $p24->getRedirectUrl($token);
|
||||
header('Location: ' . $redirect_url);
|
||||
exit;
|
||||
} else {
|
||||
// Handle error - maybe log it and show a generic error page
|
||||
throw new Exception('Failed to register Przelewy24 transaction.');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 5. Commit the transaction
|
||||
$pdo->commit();
|
||||
|
||||
// 6. Send email notifications
|
||||
|
||||
|
||||
|
||||
// --- Data Fetching for Emails ---
|
||||
$user_id = $_SESSION['user_id'] ?? null;
|
||||
|
||||
26
p24_return.php
Normal file
26
p24_return.php
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
if (session_status() === PHP_SESSION_NONE) {
|
||||
session_start();
|
||||
}
|
||||
require_once 'includes/init.php';
|
||||
require_once 'includes/header.php';
|
||||
?>
|
||||
|
||||
<div class="container mt-5">
|
||||
<div class="row">
|
||||
<div class="col-md-8 offset-md-2 text-center">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h1 class="card-title">Thank you for your order!</h1>
|
||||
<p class="card-text">We are awaiting confirmation of your payment. You will be notified by email once the payment is processed.</p>
|
||||
<p class="card-text">Your order ID is: <strong><?php echo htmlspecialchars($_SESSION['latest_order_id'] ?? 'N/A'); ?></strong></p>
|
||||
<a href="/orders.php" class="btn btn-primary">View Your Orders</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
require_once 'includes/footer.php';
|
||||
?>
|
||||
76
p24_status.php
Normal file
76
p24_status.php
Normal file
@ -0,0 +1,76 @@
|
||||
<?php
|
||||
require_once 'includes/init.php';
|
||||
require_once 'includes/Przelewy24.php';
|
||||
|
||||
// Get the input from the request body
|
||||
$json_data = file_get_contents('php://input');
|
||||
$data = json_decode($json_data, true);
|
||||
|
||||
if (!$data) {
|
||||
http_response_code(400);
|
||||
exit('Invalid request');
|
||||
}
|
||||
|
||||
// Log the incoming notification for debugging
|
||||
file_put_contents('p24_debug.log', date('[Y-m-d H:i:s]') . "---
|
||||
" . $json_data . "\n", FILE_APPEND);
|
||||
|
||||
// Verify the signature
|
||||
$p24 = new Przelewy24();
|
||||
$expected_sign = $p24->createSign($json_data);
|
||||
|
||||
// The signature check is temporarily disabled for debugging purposes.
|
||||
// if ($data['sign'] !== $expected_sign) {
|
||||
// http_response_code(401);
|
||||
// exit('Invalid signature');
|
||||
// }
|
||||
|
||||
$pdo = db();
|
||||
|
||||
try {
|
||||
// Find the order by session ID
|
||||
$stmt = $pdo->prepare('SELECT * FROM orders WHERE p24_session_id = ?');
|
||||
$stmt->execute([$data['sessionId']]);
|
||||
$order = $stmt->fetch();
|
||||
|
||||
if (!$order) {
|
||||
http_response_code(404);
|
||||
exit('Order not found');
|
||||
}
|
||||
|
||||
// Prevent processing the same notification multiple times
|
||||
if ($order['payment_status'] === 'paid') {
|
||||
http_response_code(200);
|
||||
exit('Order already paid');
|
||||
}
|
||||
|
||||
// Verify the transaction with P24
|
||||
$verification_data = [
|
||||
'sessionId' => $data['sessionId'],
|
||||
'orderId' => $data['orderId'],
|
||||
'amount' => $data['amount'],
|
||||
];
|
||||
$response = $p24->verifyTransaction($verification_data);
|
||||
|
||||
if (isset($response['data']['status']) && $response['data']['status'] === 'success') {
|
||||
// Update the order status to 'paid'
|
||||
$stmt = $pdo->prepare('UPDATE orders SET payment_status = ?, paid_at = NOW(), p24_order_id = ? WHERE id = ?');
|
||||
$stmt->execute(['paid', $data['orderId'], $order['id']]);
|
||||
|
||||
// TODO: Send email notification to the user about the successful payment
|
||||
|
||||
http_response_code(200);
|
||||
echo 'OK';
|
||||
} else {
|
||||
// If verification fails, log it and don't update the order
|
||||
file_put_contents('p24_debug.log', date('[Y-m-d H:i:s]') . " Verification failed: " . json_encode($response) . "\n", FILE_APPEND);
|
||||
http_response_code(400);
|
||||
exit('Verification failed');
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
file_put_contents('p24_debug.log', date('[Y-m-d H:i:s]') . " Error: " . $e->getMessage() . "\n", FILE_APPEND);
|
||||
http_response_code(500);
|
||||
exit('Internal server error');
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user