Advertisement
  1. Code
  2. PayPal

Integración de PayPal, Parte 3: Webhooks de PayPal

Scroll to top
Read Time: 8 min
This post is part of a series called PayPal Integration.
PayPal Integration Part 2: PayPal REST API

Spanish (Español) translation by Eva Collados Pascual (you can also view the original English article)

Final product imageFinal product imageFinal product image
What You'll Be Creating

Un Webhook es una llamada de respuesta que ocurre cuando sucede un algo en concreto; un simple sistema de notificación de eventos a través de HTTP POST que permite a los desarrolladores acceder con facilidad a las notificaciones en las actividades de pagos, como podría ser la actualización del estado de un cobro, o cargos a cuenta periódicos. Puedes realizar acciones en tu back-end tras procesar cada notificación, como por ejemplo:

  • Enviar una confirmación de pago a través de correo electrónico a tu cliente.
  • Habilitar la descarga de un archivo digital (p. ej. una imagen, un pdf, un audio, un eBook, etc.) 
  • Resolver una devolución.
  • Hacer seguimiento de qué suscriptores son en activos. 

Para crear un Webhook, dirígete al Escritorio de PayPal, pulsa sobre Mi App y Credenciales. Después selecciona la app en la que quieras configurar los Webhooks.

PayPal DeveloperPayPal DeveloperPayPal Developer

Podrás ver los detalles de tu aplicación. Advierte en la parte superior derecha, que existen dos botones (Sandbox y Live), yo voy a usar en este tutorial Sandbox, pero tendrás que establecer los ajustes para Live antes de hacer el acceso público. Para configurar los Webhooks para esta app, pulsa Añadir Webhook tal y como se muestra en la siguiente captura de pantalla:

Adding a WebHookAdding a WebHookAdding a WebHook

Selecciona los Tipos de Eventos sobre los que deseas recibir notificaciones, e introduce la URL a la que los Webhooks serán enviados (debe ser HTTPS). Para manejar las llamadas de los Webhooks, voy a añadir un nuevo método nueva acción al HomeController llamado 'Webhook':

1
public IActionResult Webhook()
2
{
3
    // TODO: Handle Webhook call
4
}

Así que la Url del Webhook en este caso será: https://pedroalonso.localtunnel.me/home/webhook. Explicaré la parte relativa a 'localtunnel' en la siguiente sección.

PayPal Developer DashboardPayPal Developer DashboardPayPal Developer Dashboard

Cuando guardes el Webhook, verás la siguiente pantalla de confirmación:

Webhook Created SuccessfullyWebhook Created SuccessfullyWebhook Created Successfully

Ahora que el Webhook está configurado, podrás ver en el menú de la izquierda, bajo 'Webhooks Simulator', aquí podrás enviar las 'pruebas' de los Webhooks para eventos a tu URL y así comprobar si el código está funcionando correctamente. Además, bajo 'Webhooks Events' puedes ver todos los eventos que PayPal ha enviado a esta aplicación. Podrás verificar si estás gestionando los eventos correctamente y reenviarlos si deseas realizar más pruebas.

Para ver cómo están funcionando los Webhooks, he ejecutado el proyecto que construimos en el tutorial anterior, y he creado un 'authorize payment and capture later' (autorizar primero y capturar después), de forma que PayPal pueda enviar el evento. Tras ejecutar la muestra, he pulsado sobre 'Webhooks Event' y he comprobado que el evento ha sido enviado:

Webhook EventsWebhook EventsWebhook Events

Puedes ver que a la derecha hay un botón Resend (reenviar) por si quieres depurar tu código y ver como implementar el manejador adecuadamente. Además, si pulsas sobre un evento, podrás ver todos sus detalles:

Webhook Event DetailsWebhook Event DetailsWebhook Event Details

Este es el JSON completo para el Webhook Event:

1
{
2
    "id": "WH-9U51749144910293K-8LX80763BC1567402",
3
  "create_time": "2016-01-19T17:50:30Z",
4
	"resource_type": "sale",
5
	"event_type": "PAYMENT.SALE.COMPLETED",
6
	"summary": "Payment completed for $ 100.0 USD",
7
	"resource": {
8
		"amount": {
9
			"total": "100.00",
10
			"currency": "USD",
11
			"details": {
12
				"subtotal": "100.00",
13
				"tax": "15.00",
14
				"shipping": "10.00"
15
			}
16
		},
17
		"id": "73G8209522783053E",
18
		"parent_payment": "PAY-7MB27930V5981832YK2PHN7Q",
19
		"update_time": "2016-01-19T17:49:05Z",
20
		"create_time": "2016-01-19T17:49:05Z",
21
		"payment_mode": "INSTANT_TRANSFER",
22
		"state": "completed",
23
		"links": [
24
			{
25
				"href": "https://api.sandbox.paypal.com/v1/payments/sale/73G8209522783053E",
26
				"rel": "self",
27
				"method": "GET"
28
			},
29
			{
30
				"href": "https://api.sandbox.paypal.com/v1/payments/sale/73G8209522783053E/refund",
31
				"rel": "refund",
32
				"method": "POST"
33
			},
34
			{
35
				"href": "https://api.sandbox.paypal.com/v1/payments/payment/PAY-7MB27930V5981832YK2PHN7Q",
36
				"rel": "parent_payment",
37
				"method": "GET"
38
			}
39
		],
40
		"protection_eligibility_type": "ITEM_NOT_RECEIVED_ELIGIBLE,UNAUTHORIZED_PAYMENT_ELIGIBLE",
41
		"transaction_fee": {
42
			"value": "3.20",
43
			"currency": "USD"
44
		},
45
		"protection_eligibility": "ELIGIBLE"
46
	},
47
	"status": "PENDING",
48
	"transmissions": [
49
		{
50
			"webhook_url": "https://pedroalonso.localtunnel.me/home/webhook",
51
			"response_headers": {
52
				"Date": "Wed, 20 Jan 2016 12:53:51 GMT",
53
				"Content-Length": "53",
54
				"HTTP/1.1 502 Bad Gateway": "",
55
				"SERVER_INFO": "",
56
				"Connection": "keep-alive",
57
				"Server": "nginx/1.7.8"
58
			},
59
			"transmission_id": "218dc9c0-bed5-11e5-927f-6b62a8a99ac4",
60
			"status": "PENDING",
61
			"timestamp": "2016-01-19T17:50:30Z"
62
		}
63
	],
64
	"links": [
65
		{
66
			"href": "https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-9U51749144910293K-8LX80763BC1567402",
67
			"rel": "self",
68
			"method": "GET",
69
			"encType": "application/json"
70
		},
71
		{
72
			"href": "https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-9U51749144910293K-8LX80763BC1567402/resend",
73
			"rel": "resend",
74
			"method": "POST",
75
			"encType": "application/json"
76
		}
77
	]
78
}

Como puedes ver en la imagen, los detalles del evento están codificados en JSON y son enviados en el cuerpo de la petición para tu Webhook manejador de las URLs. Además, hay varias propiedades importantes que debemos usar en nuestro manejador: 

  • id: Se trata del id del evento del webhook, y necesitamos enviar este parámetro a PayPal si queremos recuperar el evento específico de un webhook.
  • event_type: Se usa para conocer el tipo de evento que estamos recibiendo ya que probablemente necesitaremos procesar los diferentes tipos de eventos de forma diferente.
  • resource.parent_payment: Se trata de la id del pago al que el evento hace referencia. Posiblemente tengamos esta id almacenada en la base de datos, y podamos enviar un email a nuestro cliente o enviar los bienes que este haya adquirido.

Basándonos en la explicación previa, este es el código del controlador de la acción para procesar el Webhook:

1
public IActionResult Webhook()
2
{
3
    // The APIContext object can contain an optional override for the trusted certificate.

4
    var apiContext = PayPalConfiguration.GetAPIContext();
5
6
    // Get the received request's headers

7
    var requestheaders = HttpContext.Request.Headers;
8
9
    // Get the received request's body

10
    var requestBody = string.Empty;
11
    using (var reader = new System.IO.StreamReader(HttpContext.Request.Body))
12
    {
13
        requestBody = reader.ReadToEnd();
14
    }
15
16
    dynamic jsonBody = JObject.Parse(requestBody);
17
    string webhookId = jsonBody.id;
18
    var ev = WebhookEvent.Get(apiContext, webhookId);
19
    
20
    // We have all the information the SDK needs, so perform the validation.

21
    // Note: at least on Sandbox environment this returns false.

22
    // var isValid = WebhookEvent.ValidateReceivedEvent(apiContext, ToNameValueCollection(requestheaders), requestBody, webhookId);

23
    
24
    switch (ev.event_type)
25
    {
26
        case "PAYMENT.CAPTURE.COMPLETED":
27
            // Handle payment completed

28
            break;
29
        case "PAYMENT.CAPTURE.DENIED":
30
            // Handle payment denied

31
            break;
32
            // Handle other webhooks

33
        default:
34
            break;
35
    }
36
37
    return new HttpStatusCodeResult(200);
38
}

Hay algunas cosas que explicar sobre la función anterior. De la línea 10 a la 16, lo único que hago es leer el cuerpo de la petición y procesar el objeto JSON a un objeto dinámico C#. En la línea 18, que es opcional, estoy llamando a la API de PayPal con el Id de un evento para obtener todos sus detalles. Esto se hace por motivos de seguridad, para verificar que estoy empleando un objeto PayPal válido. En la línea 24 he creado un cambio para evaluar los tipos de Webhooks que quiero procesar y también he escrito código personalizado según lo necesitaba.

Como también puedes ver, la línea 22 tiene un comentario. Aparentemente este método valida que el certificado SSL de la solicitud (Request) sea válido y pertenezca a PayPal, pero no funciona en modo Sandbox. Podría funcionar en vivo, pero no me gusta exponer código en vivo que no haya sido previamente comprobado, especialmente si está manejando la pasarela de Pago de PayPal, así que opté por eliminarlo y usar un enfoque diferente. Si usas la versión PHP de la librería SDK de PayPal, ten en cuenta que la función 'ValidateReceivedEvent' ni siquiera existe.

Comprobar los Webhooks: Tunel de Seguridad

Tal y como has visto anteriormente, para comprobar los Webhooks, necesitamos configurar una URL pública que PayPal pueda usar para enviar eventos. Si estamos trabajando localmente, normalmente desarrollaremos usando 'localhost', así que esto constituirá un pequeño problema. Para resolverlo, necesitamos configurar un camino seguro hacia nuestro ordenador local.

Localtunnel es una pequeña pieza de software que crea un túnel seguro entre tu máquina local y el dominio de acceso público. Es útil para comprobar Webhooks, pero además lo puedes usar para compartir URLs públicas con aplicaciones web que se estén ejecutando en tu ordenador de desarrollo con el fin de realizar pruebas, obtener "feedback", u otras tareas.

Necesitarás tener Node.js para poder instalar localtunnel. Después simplemente abre la consola o el terminal, y ejecuta:

$ npm install -g localtunnel

Para crear un tunnel, ejecuta:

$ lt --port 5000 --subdomain pedroalonso

Esto mapeará la URL 'https://pedroalonso.localtunnel.me' hacia 'localhost:5000'. Si procesas tu proyecto en IIS Express, probablemente usarás un puerto diferente, así que necesitarás reflejarlo en tus comandos.

Cuando localtunnel ya haya sido configurado y tu proyecto esté en funcionamiento, he añadido un 'breakpoint' en Visual Studio para evaluar los datos que estoy obteniendo. Como puedes ver en esta captura de pantalla, he mapeado el evento JSON hacia un objeto dinámico C#.

Event Mapped to Dynamic OjectEvent Mapped to Dynamic OjectEvent Mapped to Dynamic Oject

Recuperando el evento desde la API de PayPal mediante la Id del evento, también hemos obtenido los detalles del mismo, tal y como puedes ver aquí:

WebHook EventWebHook EventWebHook Event

Conclusión

Los Webhooks se están convirtiendo en un modo estándar para que la REST API notifique a las aplicaciones sobre los eventos. Como puedes ver, se manejan de forma muy sencilla, y son empleados por muchas compañías como Stripe, SendGrid, MailChimp, etc. PayPal solía disponer de Notificaciones Instantáneas de Pagos, y todavía está disponible, pero ellos mismos recomiendan implementar con preferencia y siempre que sea posible los Webhooks.

Creo que sería realmente interesante si más aplicaciones dirigidas al consumidor ofrecieran Webhooks. La habilidad de comenzar un proceso basado en un evento de una aplicación externa es extremadamente útil y ofrece una visión fugaz sobre el futuro de la web a tiempo real.

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.