Skip to content

Commit 2cb1ba8

Browse files
committed
Primer commit - módulo PayU para WHMCS
0 parents  commit 2cb1ba8

3 files changed

Lines changed: 262 additions & 0 deletions

File tree

README.MD

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# Módulo PayU Latam para WHMCS
2+
3+
Este es un **módulo sencillo** para integrar PayU Latam en WHMCS mediante **Web Checkout**.
4+
5+
⚠️ **Importante:** Este módulo es básico y no profesional. Puede requerir ajustes según tu versión de WHMCS o necesidades específicas. Úsalo bajo tu propia responsabilidad. Aunque está implementado con firma SHA-256 en el callback y MD5 en el formulario, no hay garantía absoluta de seguridad frente a configuraciones incorrectas.
6+
7+
---
8+
9+
## Archivos incluidos
10+
11+
- `payu.php` → Lógica principal del módulo y generación del formulario de pago.
12+
- `callback/payu.php` → Callback que procesa los pagos recibidos desde PayU.
13+
14+
---
15+
16+
## Instalación
17+
18+
1. Subir los archivos a tu instalación de WHMCS:
19+
20+
modules/gateways/payu.php
21+
modules/gateways/callback/payu.php
22+
23+
24+
2. Ingresar al **WHMCS Admin Panel**:
25+
26+
Setup → Payments → Payment Gateways → Activate/Configure
27+
28+
3. Activar **PayU Latam** y llenar los campos:
29+
30+
| Campo | Descripción |
31+
|---------------|-------------------------------------------------|
32+
| Merchant ID | Identificador de tu comercio en PayU |
33+
| Account ID | ID de cuenta asignado por PayU |
34+
| API Key | Clave secreta proporcionada por PayU |
35+
| Test Mode | Activar modo pruebas si deseas simular pagos |
36+
37+
4. Guardar la configuración.
38+
39+
---
40+
41+
## Uso
42+
43+
1. Al generar una factura en WHMCS, el módulo mostrará un botón:
44+
45+
[Pagar con PayU]
46+
47+
2. El cliente será redirigido a PayU con un **formulario seguro**, incluyendo:
48+
49+
- `merchantId`
50+
- `accountId`
51+
- `amount`
52+
- `currency`
53+
- `description`
54+
- `referenceCode` (único por transacción)
55+
- `signature` (MD5)
56+
- `responseUrl` y `confirmationUrl`
57+
58+
3. PayU procesará el pago y enviará la confirmación al **callback**:
59+
60+
- Se valida la **firma SHA-256**
61+
- Se verifica que la transacción esté aprobada (`state_pol = 4`)
62+
- Se registra el pago en WHMCS
63+
64+
---
65+
66+
## Seguridad
67+
68+
- Las credenciales reales **no se almacenan en el código**, sino en la **base de datos de WHMCS** (`tblpaymentgateways`).
69+
- El callback utiliza **SHA-256** para validar la autenticidad de los pagos.
70+
- La referencia de la factura (`referenceCode`) se genera **única por intento**, evitando pagos duplicados.
71+
72+
---
73+
74+
## Limitaciones
75+
76+
- Módulo **básico**: puede requerir cambios según tu versión de WHMCS o PayU.
77+
- No incluye manejo avanzado de errores o devoluciones automáticas.
78+
- Es responsabilidad del usuario mantener **seguras las credenciales**.
79+
- La firma del formulario sigue siendo **MD5**, que es suficiente para Web Checkout, pero no es ideal para seguridad máxima.
80+
81+
---
82+
83+
## Conclusión
84+
85+
Este módulo es **100% funcional** y debería procesar pagos correctamente si se configura bien, pero:
86+
87+
- Úsalo bajo tu **propia responsabilidad**
88+
- No está pensado para producción de alta seguridad sin revisión adicional
89+
- Es recomendable probar primero en **modo sandbox** antes de activar pagos reales.
90+
91+
---
92+
93+
## Créditos
94+
95+
Desarrollado por Panda G con asistencia de ChatGPT.
96+
Inspirado en la documentación oficial de **PayU Latam**.

modules/callback/payu_callback.php

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php
2+
require_once '../../../init.php';
3+
require_once '../../../includes/gatewayfunctions.php';
4+
require_once '../../../includes/invoicefunctions.php';
5+
6+
$gatewayModuleName = "payu";
7+
$gatewayParams = getGatewayVariables($gatewayModuleName);
8+
9+
if (!$gatewayParams['type']) {
10+
die("Módulo no activado");
11+
}
12+
13+
// Datos recibidos de PayU //
14+
$referenceCode = $_POST['reference_sale'] ?? '';
15+
$transactionId = $_POST['transaction_id'] ?? '';
16+
$statePol = $_POST['state_pol'] ?? '';
17+
$amount = $_POST['value'] ?? 0;
18+
$currency = $_POST['currency'] ?? '';
19+
$sign = $_POST['sign'] ?? '';
20+
21+
// Normalizar monto según moneda
22+
if (in_array($currency, ["COP", "CLP", "ARS"])) {
23+
$amount = number_format($amount, 0, '.', '');
24+
} else {
25+
$amount = number_format($amount, 2, '.', '');
26+
}
27+
28+
// Firma SHA-256 para validar callback //
29+
$apiKey = $gatewayParams['apiKey'];
30+
$merchantId = $gatewayParams['merchantId'];
31+
$signatureCheck = hash('sha256', "{$apiKey}~{$merchantId}~{$referenceCode}~{$amount}~{$currency}~{$statePol}");
32+
33+
// Extraer invoiceId de la referencia única //
34+
if (preg_match("/INV([0-9]+)/", $referenceCode, $matches)) {
35+
$invoiceId = (int)$matches[1];
36+
} else {
37+
logTransaction($gatewayModuleName, $_POST, "Error: invoiceId no válido en referencia");
38+
die("ERROR");
39+
}
40+
41+
// Validar que la factura existe //
42+
checkCbInvoiceID($invoiceId, $gatewayParams['name']);
43+
44+
// Prevenir duplicados //
45+
checkCbTransID($transactionId);
46+
47+
// Validar firma y estado aprobado (4 = aprobado) //
48+
if (hash_equals($signatureCheck, $sign) && $statePol == 4) {
49+
addInvoicePayment(
50+
$invoiceId,
51+
$transactionId,
52+
$amount,
53+
0,
54+
$gatewayModuleName
55+
);
56+
57+
logTransaction($gatewayModuleName, $_POST, "Pago aprobado");
58+
echo "OK";
59+
} else {
60+
logTransaction($gatewayModuleName, $_POST, "Firma inválida o estado no aprobado");
61+
echo "ERROR";
62+
}

modules/gateways/payu.php

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
<?php
2+
if (!defined("WHMCS")) {
3+
die("Este archivo no puede ejecutarse directamente");
4+
}
5+
6+
function payu_MetaData()
7+
{
8+
return [
9+
'DisplayName' => 'PayU Latam',
10+
'APIVersion' => '1.1',
11+
];
12+
}
13+
14+
function payu_config()
15+
{
16+
return [
17+
'FriendlyName' => [
18+
'Type' => 'System',
19+
'Value' => 'PayU Latam',
20+
],
21+
'merchantId' => [
22+
'FriendlyName' => 'Merchant ID',
23+
'Type' => 'text',
24+
'Size' => '25',
25+
],
26+
'accountId' => [
27+
'FriendlyName' => 'Account ID',
28+
'Type' => 'text',
29+
'Size' => '25',
30+
],
31+
'apiKey' => [
32+
'FriendlyName' => 'API Key',
33+
'Type' => 'password',
34+
'Size' => '40',
35+
],
36+
'testMode' => [
37+
'FriendlyName' => 'Modo Pruebas',
38+
'Type' => 'yesno',
39+
],
40+
];
41+
}
42+
43+
function payu_link($params)
44+
{
45+
$merchantId = $params['merchantId'];
46+
$accountId = $params['accountId'];
47+
$apiKey = $params['apiKey'];
48+
$testMode = $params['testMode'];
49+
50+
$currency = "USD";
51+
$amount = number_format($params['amount'], 2, '.', '');
52+
53+
$invoiceId = $params['invoiceid'];
54+
$description = "Pago Factura #" . $invoiceId;
55+
56+
// Generar referencia única //
57+
$referenceCode = "INV" . $invoiceId . "-" . time();
58+
$buyerEmail = $params['clientdetails']['email'];
59+
60+
$responseUrl = rtrim($params['systemurl'], '/') . '/viewinvoice.php?id=' . $invoiceId;
61+
$confirmationUrl = rtrim($params['systemurl'], '/') . '/modules/gateways/callback/payu.php';
62+
63+
// Firma MD5 para Web Checkout //
64+
$signature = md5("$apiKey~$merchantId~$referenceCode~$amount~$currency");
65+
66+
$actionUrl = $testMode
67+
? "https://sandbox.checkout.payulatam.com/ppp-web-gateway-payu/"
68+
: "https://checkout.payulatam.com/ppp-web-gateway-payu/";
69+
70+
$html = '<form method="post" action="' . htmlspecialchars($actionUrl, ENT_QUOTES, 'UTF-8') . '" style="text-align:center;">';
71+
$html .= '<input type="hidden" name="merchantId" value="' . htmlspecialchars($merchantId, ENT_QUOTES, 'UTF-8') . '">';
72+
$html .= '<input type="hidden" name="accountId" value="' . htmlspecialchars($accountId, ENT_QUOTES, 'UTF-8') . '">';
73+
$html .= '<input type="hidden" name="description" value="' . htmlspecialchars($description, ENT_QUOTES, 'UTF-8') . '">';
74+
$html .= '<input type="hidden" name="referenceCode" value="' . htmlspecialchars($referenceCode, ENT_QUOTES, 'UTF-8') . '">';
75+
$html .= '<input type="hidden" name="amount" value="' . $amount . '">';
76+
$html .= '<input type="hidden" name="tax" value="0">';
77+
$html .= '<input type="hidden" name="taxReturnBase" value="0">';
78+
$html .= '<input type="hidden" name="currency" value="' . $currency . '">';
79+
$html .= '<input type="hidden" name="signature" value="' . $signature . '">';
80+
$html .= '<input type="hidden" name="test" value="' . ($testMode ? "1" : "0") . '">';
81+
$html .= '<input type="hidden" name="buyerEmail" value="' . htmlspecialchars($buyerEmail, ENT_QUOTES, 'UTF-8') . '">';
82+
$html .= '<input type="hidden" name="responseUrl" value="' . htmlspecialchars($responseUrl, ENT_QUOTES, 'UTF-8') . '">';
83+
$html .= '<input type="hidden" name="confirmationUrl" value="' . htmlspecialchars($confirmationUrl, ENT_QUOTES, 'UTF-8') . '">';
84+
$html .= '<button type="submit" style="
85+
display:inline-flex;
86+
align-items:center;
87+
gap:8px;
88+
background-color:#d4edda;
89+
border:1px solid #28a745;
90+
border-radius:6px;
91+
padding:4px 8px;
92+
cursor:pointer;
93+
font-family:Arial, sans-serif;
94+
font-size:14px;
95+
">
96+
<img src="https://upload.wikimedia.org/wikipedia/commons/c/cd/PayU.svg"
97+
alt="PayU"
98+
style="height:24px; width:auto;">
99+
<span style="color:#155724; font-weight:bold;">Pagar con PayU</span>
100+
</button>';
101+
$html .= '</form>';
102+
103+
return $html;
104+
}

0 commit comments

Comments
 (0)