Carrito de la compra integrado con Paypal mediante PHP

En tutoriales anteriores vimos cómo crear un carrito de la compra con Ajax, PHP y MySQL en el que podíamos añadir elementos dinámicamente. Pero como bien sabes, todavía necesita un empujón para convertirse en un carrito de la compra de verdad. Por eso, en este tutorial, aprenderemos a integrar una pasarela de pago Paypal dentro de un carrito para gestionar los pagos de nuestra web. Usaremos el mismo carrito del anterior tutorial para este ejemplo. Es decir, utilizaremos las funcionalidades de mostrar los detalles del carrito y de revisar la información antes de realizar el pedido con el importe. Así que ahora nos encargaremos de obtener la info de envío del cliente y así gestionar el pago mediante nuestra nueva pasarela de pago Paypal.

Crear una cuenta Sandbox en Paypal

Como antes de integrar nuestra pasarela de pago necesitamos realizar pruebas, necesitamos una cuenta SandBox de Paypal. Así que debes ingresar con tus datos en la plataforma de Paypal y crear una cuenta de prueba, es decir, una Sandbox. Cuando ya hayamos realizado los tests, ingresaremos los datos de la cuenta en real.

Crear tablas de la base de datos

En este tutorial utilizaremos las siguientes tablas MySQL para gestionar nuestro carrito de la compra.

La información de nuestros productos se almacenarán dentro de la tabla shop_products.

CREATE TABLE `shop_products` (
`id` int(11) NOT NULL,
`product_name` varchar(60) NOT NULL,
`product_desc` text NOT NULL,
`product_code` varchar(60) NOT NULL,
`product_image` varchar(60) NOT NULL,
`product_price` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

La información de los pedidos se almacenarán en la tabla shop_order.

CREATE TABLE `shop_order` (
`id` int(11) NOT NULL,
`member_id` int(11) NOT NULL,
`name` varchar(100) NOT NULL,
`address` varchar(100) NOT NULL,
`mobile` int(11) NOT NULL,
`email` varchar(100) NOT NULL,
`order_status` varchar(255) NOT NULL,
`order_at` datetime NOT NULL,
`payment_type` varchar(255) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

Las líneas de cada pedido se almacenarán en la tabla shop_order_item.

CREATE TABLE `shop_order_item` (
`id` int(11) NOT NULL,
`order_id` int(11) NOT NULL,
`product_id` int(11) NOT NULL,
`item_price` double NOT NULL,
`quantity` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

Y la información relacionada con los pagos se almacenarán en la tabla shop_payment.

CREATE TABLE `shop_payment` (
`id` int(11) NOT NULL,
`order_id` int(11) NOT NULL,
`payment_status` varchar(255) NOT NULL,
`payment_response` text NOT NULL,
`create_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

Información sobre el envío

Como ya tenemos cubierto el hecho de agregar productos al carrito y de hacer el checkout, vamos a seguir con el tema de la información sobre el envío del pedido de nuestro cliente. Para ello diseñaremos un formulario HTML en user.php para solicitar al cliente que introduzca los datos donde tenemos que enviar el pedido.

<h2>Customer Shipping Details</h2>
<div class="col-md-8">
<form class="form-horizontal" method="post" enctype="multipart/form-data" action="payment.php">
<div class="form-group">
<div class="col-sm-6">
<input type="text" class="form-control" placeholder="First Name" name="firstName" required />
</div>
</div>
<div class="form-group">
<div class="col-sm-6">
<input type="text" class="form-control" placeholder="Last Name" name="lastName" required />
</div>
</div>
<div class="form-group">
<div class="col-sm-6">
<textarea class="form-control" rows="5" placeholder="Address" name="address" required ></textarea>
</div>
</div>
<div class="form-group">
<div class="col-sm-8">
<input type="number" class="form-control" min="9" placeholder="Contact number" name="contactNumber" required />
</div>
</div>
<div class="form-group">
<div class="col-sm-6">
<input type="email" class="form-control" placeholder="Email" name="emailAddress" required />
</div>
</div>
<div class="form-group">
<div class="col-sm-4">
<input class="btn btn-primary" type="submit" name="proceedPayment" value="Proceed to payment"/>
</div>
</div>
</form>
</div>

Guardar la información del pedido

Ahora gestionaremos la función de guardar la información de nuestro pedido en la tabla shop_order junto a la información que acabamos de recibir.

<?php
if(!empty($_POST["proceedPayment"])) {
$firstName = $_POST ['firstName'];
$lastName = $_POST ['lastName'];
$address = $_POST ['address'];
$contactNumber = $_POST ['contactNumber'];
$emailAddress = $_POST ['emailAddress'];
$insertOrderSQL = "INSERT INTO shop_order(member_id, name, address, mobile, email, order_status, order_at, payment_type)VALUES('".$member_id."', '".$firstName." ".$lastName."', '".$address."', '".$contactNumber."', '".$emailAddress."', 'PENDING', '".date("Y-m-d H:i:s")."', 'PAYPAL')";
mysqli_query($conn, $insertOrderSQL) or die("database error:". mysqli_error($conn));
$order_id = mysqli_insert_id($conn);
}
?>

Logicamente, también guardaremos las líneas del pedido en la tabla shop_order_item.

<?php
if($order_id) {
if(isset($_SESSION["products"]) && count($_SESSION["products"])>0) {
foreach($_SESSION["products"] as $product){
$insertOrderItem = "INSERT INTO shop_order_item(order_id, product_id, item_price, quantity)VALUES('".$order_id."', '".$product["product_code"]."', '". $product["product_price"]."', '".$product["product_qty"]."')";
mysqli_query($conn, $insertOrderItem) or die("database error:". mysqli_error($conn));
}
}
}?>

Envío de datos a Paypal

Ahora en payment.php, gestionaremos el envío de la información del pedido a nuestra pasarela Paypal. Los campos requeridos son item_number, amount, currency_code, notify_url, return_url. Dichos campos se los tendremos que enviar a Paypal para gestionar el pago.

<form class="form-horizontal" action="https://www.sandbox.paypal.com/cgi-bin/webscr" method="POST">
<input type='hidden' name='business' value='Your Business Email Address'>
<input type='hidden' name='item_name' value='<?php echo $_SESSION["cartItems"]; ?>'>
<input type='hidden' name='item_number' value="<?php echo $order_id; ?>">
<input type='hidden' name='amount' value='<?php echo $_SESSION["payableAmount"]; ?>'>
<input type='hidden' name='currency_code' value='USD'>
<input type='hidden' name='notify_url' value='http://yourdomain.com/shopping-cart-with-paypal-integration/notify.php'>
<input type='hidden' name='return' value='http://yourdomain.com/shopping-cart-with-paypal-integration/success.php'>
<input type="hidden" name="cmd" value="_xclick">
<input type="hidden" name="order" value="<?php echo $_SESSION["orderNumber"]; ?>">
<br>
<div class="form-group">
<div class="col-sm-2">
<input type="submit" class="btn btn-lg btn-block btn-danger" name="continue_payment" value="Pay Now">
</div>
</div>
</form>

Proceso de pago

Cuando el usuario pulse el botón Pay Now, será redirigido a la pasarela de pago de Paypal para efectuar el pedido. Si el pago ha sido satisfactorio, se llamara al fichero que le hemos pasado en el campo notify_url (en nuestro caso notify.php) para comunicar a nuestra plataforma el estado del pedido.

<?php
include_once("../db_connect.php");
$postData = file_get_contents('php://input');
$postArray = explode('&', $postData);
$postValue = array();
foreach ($postArray as $value) {
$value = explode ('=', $value);
if (count($value) == 2)
$postValue[$value[0]] = urldecode($value[1]);
}
// read the post from PayPal system and add 'cmd'
$req = 'cmd=_notify-validate';
if(function_exists('get_magic_quotes_gpc')) {
$get_magic_quotes_exists = true;
}
foreach ($postValue as $key => $value) {
if($get_magic_quotes_exists == true && get_magic_quotes_gpc() == 1) {
$value = urlencode(stripslashes($value));
} else {
$value = urlencode($value);
}
$req .= "&$key=$value";
}
// The post IPN data back to PayPal to validate the IPN data
$ch = curl_init("https://www.sandbox.paypal.com/cgi-bin/webscr");
if ($ch == FALSE) {
return FALSE;
}
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $req);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_FORBID_REUSE, 1);
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLINFO_HEADER_OUT, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Connection: Close'));
$res = curl_exec($ch);
if (curl_errno($ch) != 0) {
error_log(date('[Y-m-d H:i e] '). "Can't connect to PayPal to validate IPN message: " . curl_error($ch) . PHP_EOL, 3, 'app.log');
curl_close($ch);
exit;
} else {
error_log(date('[Y-m-d H:i e] '). "HTTP request of validation request:". curl_getinfo($ch, CURLINFO_HEADER_OUT) ." for IPN payload: $req" . PHP_EOL, 3, 'app.log');
error_log(date('[Y-m-d H:i e] '). "HTTP response of validation request: $res" . PHP_EOL, 3, 'app.log');
curl_close($ch);
}
// Inspect IPN validation result and act accordingly
$payment_response = $res;
$tokens = explode("rnrn", trim($res));
$res = trim(end($tokens));
if (strcmp ($res, "VERIFIED") == 0) {
$item_name = $_POST['item_name'];
$item_number = $_POST['item_number'];
$payment_status = $_POST['payment_status'];
$payment_amount = $_POST['mc_gross'];
$payment_currency = $_POST['mc_currency'];
$txn_id = $_POST['txn_id'];
$receiver_email = $_POST['receiver_email'];
$payer_email = $_POST['payer_email'];
$isPaymentCompleted = false;
if($payment_status == "Completed") {
$isPaymentCompleted = true;
}
// insert payment details
$insertPayment = "INSERT INTO shop_payment(order_id, payment_status, payment_response)VALUES('".$order_id."', '".$payment_status."', '".$payment_response."')";
mysqli_query($conn, $insertPayment) or die("database error:". mysqli_error($conn));
// update order status after payment
$updateOrder = "UPDATE shop_order set order_status = 'PAID' WHERE id = '".$item_number."'";
mysqli_query($conn, $updateOrder) or die("database error:". mysqli_error($conn));
error_log(date('[Y-m-d H:i e] '). "Verified IPN: $req ". PHP_EOL, 3, 'app.log');
} else if (strcmp ($res, "INVALID") == 0) {
error_log(date('[Y-m-d H:i e] '). "Invalid IPN: $req" . PHP_EOL, 3, 'app.log');
}
?>

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP