Files
kupshop/class/payments/class.WebPay.php
2025-08-02 16:30:27 +02:00

252 lines
10 KiB
PHP

<?php
use KupShop\KupShopBundle\Config;
/**
* Requires composer packages alcohol/iso3166, alcohol/iso4217 and adamstipak/webpay-php:~1.1.3 (https://github.com/wpj-cz/gp-webpay-php-sdk).
*/
class WebPay extends Payment
{
public static $name = 'WebPay platební brána';
// public $template = 'payment.WebPay.tpl';
public $class = 'WebPay';
public $tp_id_payment;
/** @var \AdamStipak\Webpay\Api */
public $apiContext;
public $method;
protected $pay_method = Payment::METHOD_ONLINE;
public function getPaymentUrl()
{
return $this->getGenericPaymentUrl(1);
}
public function getGatewayUrl()
{
return $this->config['webpayUrl'];
}
public function getData(): array
{
$request = $this->createPaymentRequest();
return $this->getApiContext()->createPaymentParam($request);
}
private function getPayment(bool $alwaysNew = true)
{
$kupshopPayment = $this->getPendingPayment();
if (!$alwaysNew && $kupshopPayment) {
return $kupshopPayment;
} else {
$this->createPayment(
null,
$this->order->getRemainingPayment(),
['paymentClass' => self::class]
);
$paymentRow = $this->selectSQL('order_payments', ['id' => $this->paymentId])->fetch();
$json = json_decode($paymentRow['payment_data'], true);
$json['session'] = $this->paymentId;
$paymentRow['payment_data'] = json_encode($json);
$this->updateSQL('order_payments', $paymentRow, ['id' => $this->paymentId]);
$paymentRow['decoded_data'] = $json;
return $paymentRow;
}
}
private function createPaymentRequest(): AdamStipak\Webpay\PaymentRequest
{
$iso4217 = new Alcohol\ISO4217();
if (class_exists('Alcohol\ISO3166\ISO3166')) {
$iso3166 = new Alcohol\ISO3166\ISO3166();
}
$amount = $this->order->getRemainingPayment();
if ($amount <= (float) 0) {
$this->step(-3, 'storno');
}
$addInfo = new SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?><additionalInfoRequest xmlns="http://gpe.cz/gpwebpay/additionalInfo/request" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>');
$addInfo['version'] = '4.0';
$cardHolderInfo = $addInfo->addChild('cardholderInfo');
$cardHolderDetails = $cardHolderInfo->addChild('cardholderDetails');
$cardHolderDetails->name = mb_substr($this->order->invoice_name.' '.$this->order->invoice_surname, 0, 255);
$cardHolderDetails->email = mb_substr($this->order->invoice_email, 0, 255);
if (!empty($this->order->invoice_street) && isset($iso3166)
&& !empty($this->order->invoice_name)
&& !empty($this->order->invoice_city)
&& !empty($this->order->invoice_zip)
&& !empty($this->order->invoice_country)
&& !empty($this->order->invoice_email)
) {
$billingDetails = $cardHolderInfo->addChild('billingDetails');
$billingDetails->name = mb_substr($this->order->invoice_name.' '.$this->order->invoice_surname, 0, 255);
$billingDetails->address1 = mb_substr($this->order->invoice_street, 0, 50);
if (!empty($this->order->invoice_custom_address)) {
$billingDetails->address2 = mb_substr($this->order->invoice_custom_address, 0, 50);
}
$billingDetails->city = mb_substr($this->order->invoice_city, 0, 50);
$billingDetails->postalCode = mb_substr($this->order->invoice_zip, 0, 16);
$billingDetails->country = (int) $iso3166->getByAlpha2($this->order->invoice_country)['numeric'];
// phone has weird validation rules: cvc-pattern-valid: Value '+420777852147' is not facet-valid with respect to pattern '\d{1,15}' for type 'phoneValue'.
if (!empty($this->order->invoice_phone)) {
$billingDetails->phone = mb_substr(preg_replace('/([^0-9]+)/', '', $this->order->invoice_phone), 0, 20);
}
$billingDetails->email = mb_substr($this->order->invoice_email, 0, 255);
}
if (!empty($this->order->delivery_street) && isset($iso3166)
&& !empty($this->order->delivery_name)
&& !empty($this->order->delivery_city)
&& !empty($this->order->delivery_zip)
&& !empty($this->order->delivery_country)
) {
$shippingDetails = $cardHolderInfo->addChild('shippingDetails');
$shippingDetails->name = mb_substr($this->order->delivery_name.' '.$this->order->delivery_surname, 0, 255);
$shippingDetails->address1 = mb_substr($this->order->delivery_street, 0, 50);
if (!empty($this->order->delivery_custom_address)) {
$shippingDetails->address2 = mb_substr($this->order->delivery_custom_address, 0, 50);
}
$shippingDetails->city = mb_substr($this->order->delivery_city, 0, 50);
$shippingDetails->postalCode = mb_substr($this->order->delivery_zip, 0, 16);
$shippingDetails->country = (int) $iso3166->getByAlpha2($this->order->delivery_country)['numeric'];
// phone has weird validation rules: cvc-pattern-valid: Value '+420777852147' is not facet-valid with respect to pattern '\d{1,15}' for type 'phoneValue'.
if (!empty($this->order->delivery_phone)) {
$shippingDetails->phone = mb_substr(preg_replace('/([^0-9]+)/', '', $this->order->delivery_phone), 0, 20);
}
}
$paymentInfo = $addInfo->addChild('paymentInfo');
$paymentInfo->transactionType = '01';
$shoppingCartInfo = $addInfo->addChild('shoppingCartInfo');
$shoppingCartItems = $shoppingCartInfo->addChild('shoppingCartItems');
foreach ($this->order->fetchItems() as $item) {
if ($item['piece_price']['value_without_vat']->asInteger() < 0) {
continue; // skip items with negative price (must be unsignedLong - 12 digits max)
}
$shoppingCartItem = $shoppingCartItems->addChild('shoppingCartItem');
if (!empty($item['id_product'])) {
$itemCodeValue = $item['id_product'].(!empty($item['id_variation']) ? ('_'.$item['id_variation']) : '');
if (mb_strlen($itemCodeValue) <= 20) {
$shoppingCartItem->itemCode = $itemCodeValue;
}
}
$shoppingCartItem->itemDescription = mb_substr($item['descr'], 0, 50);
$shoppingCartItem->itemQuantity = $item['pieces'];
$shoppingCartItem->itemUnitPrice = $item['piece_price']['value_without_vat']->asInteger();
}
$request = new \AdamStipak\Webpay\PaymentRequest(
$this->getPayment()['id'],
$amount,
(int) $iso4217->getByAlpha3($this->order->currency)['numeric'],
1,
$this->getGenericPaymentUrl(5),
$this->order->id
);
$request->setParam('REFERENCENUMBER', $this->order->order_no);
$request->setParam('ADDINFO', str_replace(PHP_EOL, '', $addInfo->asXML()));
return $request;
}
/* Payment steps */
public function processStep_1()
{
$this->template = 'payment.WebPay.tpl';
}
public function processStep_5()
{
$response = new \AdamStipak\Webpay\PaymentResponse(
$_REQUEST['OPERATION'],
$_REQUEST['ORDERNUMBER'],
$_REQUEST['MERORDERNUM'],
$_REQUEST['PRCODE'],
$_REQUEST['SRCODE'],
$_REQUEST['RESULTTEXT'],
$_REQUEST['DIGEST'],
$_REQUEST['DIGEST1']
);
try {
$this->getApiContext()->verifyPaymentResponse($response);
} catch (\AdamStipak\Webpay\PaymentResponseException $e) {
// change payment status to finished
if (!$this->setStatus(Payment::STATUS_STORNO, $_REQUEST['ORDERNUMBER'])) {
logError(__FILE__, __LINE__, 'WebPay::updatePaymentStatus: setStatus failed!');
exit;
}
if ($_REQUEST['PRCODE'] != 50) { // nejedna se o "Drzitel karty zrusil platbu"
// PaymentResponseException has $prCode, $srCode for properties for logging GP Webpay response error codes.
logError(__FILE__, __LINE__, 'WebPay error: '.$_REQUEST['PRCODE'].' - '.$_REQUEST['RESULTTEXT'].', exception:'.$e);
}
$this->error(translate('payment_storno', 'payment'));
} catch (Exception $e) {
// Digest is not correct.
logError(__FILE__, __LINE__, 'WebPay exception: '.$e);
$this->error(translate('payment_storno', 'payment'));
}
if ($_REQUEST['PRCODE'] == 0) {
$this->status = Payment::STATUS_FINISHED;
// change payment status to finished
if (!$this->setStatus(Payment::STATUS_FINISHED, $_REQUEST['ORDERNUMBER'])) {
logError(__FILE__, __LINE__, 'WebPay::updatePaymentStatus: setStatus failed!');
exit;
}
$this->success(translate('paymentSuccess', 'payment'));
}
}
/* Payment methods */
public function getApiContext(): AdamStipak\Webpay\Api
{
if (!isset($this->apiContext)) {
$signer = new \AdamStipak\Webpay\Signer(
$this->config['privateKeyFilepath'],
$this->config['privateKeyPassword'],
$this->config['GPpublicKeyFilepath']
);
$this->apiContext = new \AdamStipak\Webpay\Api(
$this->config['merchantNumber'],
$this->config['webpayUrl'],
$signer
);
}
return $this->apiContext;
}
public function hasOnlinePayment()
{
return true;
}
public static function isEnabled($className)
{
$cfg = Config::get();
if (empty($cfg['Modules']['payments'][$className])) {
return false;
}
return true;
}
}