# Webhooks & Notifications

Easypay uses webhooks (also called notifications) to inform your application about events that happen in your Easypay account. This is a server-to-server service that sends near real-time notifications about payment status changes.

## What are Webhooks?

Webhooks are **POST API** calls (JSON encoded) that let your application know an event has happened. They enable your system to react automatically to payment events without constantly polling the API.

## Why Use Webhooks?

Webhooks are particularly useful for asynchronous events like:

- When a customer pays a Multibanco reference
- A frequent payment succeeds/fails
- A subscription payment succeeds/fails
- A checkout single sale payment succeeds/fails
- Authorization is granted or declined
- Capture is completed


With webhooks, your system can automate custom actions in response to the transitions that happen in a payment flow.

## Supported Notification Types

Easypay supports 3 notification types:

### 1. Generic Notification

A notification informing about all state transitions of some payment resource.

**Use Case**: Comprehensive monitoring of all payment events

**Payload Example**:


```json
{
  "id": "5eca7446-14e9-47bb-aabb-5ee237159b8b",
  "key": "dcf9ab3fd95ca3d5607853f36d46f161c8715858",
  "type": "capture",
  "status": "success",
  "messages": ["Your request was successfully captured"],
  "date": "2022-08-10 14:56:54"
}
```

### 2. Authorisation Notification

A notification informing about some payment transitioning to authorization status. Available for single and frequent payments.

**Use Case**: Specific monitoring of authorization events

**Payload Example**:


```json
{
  "id": "1bbc14c3-8ca8-492c-887d-1ca86400e4fa",
  "value": 1,
  "currency": "EUR",
  "key": "the merchant key",
  "expiration_time": "2022-01-01 10:20",
  "customer": {
    "id": "22ea3cc9-424b-489a-91b7-8955f643dc93",
    "name": "Customer Example",
    "email": "customer@example.com",
    "phone": "911234567",
    "phone_indicative": "+351"
  },
  "method": "mb",
  "account": {
    "id": "4c67e74b-a256-4e0a-965d-97bf5d01bd50"
  },
  "authorisation": {
    "id": "4c67e74b-a256-4e0a-965d-97bf5d01bd50"
  }
}
```

### 3. Transaction Notification

A notification informing about some payment transitioning to capture status. Available for single and frequent payments.

**Use Case**: Specific monitoring of successful payment captures

**Payload Example**:


```json
{
  "id": "87615356-0a88-42bd-8abb-aab3e90128de",
  "value": "40",
  "currency": "EUR",
  "key": "the merchant key",
  "expiration_time": "2023-08-07 20:00",
  "method": "MBW",
  "customer": {
    "id": "2eb64a7f-90a7-4dc6-959b-1d9aba44910c",
    "phone": "910410419"
  },
  "account": {
    "id": "0b8de6e7-89c8-4d76-93e8-019bc058f27d"
  },
  "transaction": {
    "id": "eb23923b-3529-4b71-b54e-1e707a8d55c4",
    "key": "transaction_key_of_this_capture",
    "type": "capture",
    "date": "2022-08-10T12:45:50Z",
    "values": {
      "requested": "40",
      "paid": "40",
      "fixed_fee": "0",
      "variable_fee": "0",
      "tax": "0",
      "transfer": "0"
    }
  }
}
```

**Note**: If you subscribe to both Generic and Authorisation notifications, you will receive two notifications for the same event with different message contracts.

## Recommended Notification Flow

![Recommended flow](https://easypay-cdn-delivery.s3.eu-central-1.amazonaws.com/docs/checkout/notification_recommended_flow.png)

1. **Easypay sends notification** to your configured endpoint
2. **Your system receives notification** and extracts the payment ID
3. **Your system queries Easypay API** to verify the notification (recommended for security)
4. **Your system processes** the payment according to the API response


## Configuring Webhooks

### Via Backoffice

1. Log in to Easypay backoffice
2. Navigate to `Developers > Configuration API 2.0`
3. Select the payment account you want to receive notifications
4. Select `Notifications`
5. Configure your endpoints:
  - **Generic - URL**: For generic notifications
  - **Authorisation - URL**: For authorisation notifications
  - **Payment - URL**: For capture/transaction notifications


## Implementing a Webhook Endpoint

### Basic Requirements

Your webhook endpoint must:

- Accept POST requests
- Accept JSON payloads
- Respond with HTTP 200 status code
- Process requests within 20 seconds
- Be accessible over HTTPS (recommended)


### Example Implementation

Node

```javascript
const express = require('express');
const axios = require('axios');

const app = express();
app.use(express.json());

app.post('/webhooks/easypay/generic', async (req, res) => {
  const notification = req.body;

  // 1. Acknowledge receipt immediately
  res.status(200).send('OK');

  // 2. Verify the notification by querying the API
  try {
    const response = await axios.get(
      `https://api.prod.easypay.pt/2.0/single/${notification.id}`,
      {
        headers: {
          'AccountId': process.env.EASYPAY_ACCOUNT_ID,
          'ApiKey': process.env.EASYPAY_API_KEY
        }
      }
    );

    // 3. Process the verified payment
    if (response.data.status === 'success') {
      await processSuccessfulPayment(response.data);
    } else {
      await handleFailedPayment(response.data);
    }

  } catch (error) {
    console.error('Error verifying notification:', error);
    // Implement retry logic or alert system
  }
});

async function processSuccessfulPayment(payment) {
  // Your business logic here
  console.log('Processing successful payment:', payment.id);
  // Update database, send confirmation email, etc.
}

async function handleFailedPayment(payment) {
  // Your error handling logic
  console.log('Handling failed payment:', payment.id);
}

app.listen(3000);
```

Python

```python
from flask import Flask, request, jsonify
import requests
import os

app = Flask(__name__)

@app.route('/webhooks/easypay/generic', methods=['POST'])
def generic_notification():
    notification = request.json

    # 1. Acknowledge receipt immediately
    response = jsonify({'status': 'received'})
    response.status_code = 200

    # 2. Verify the notification by querying the API
    try:
        verify_response = requests.get(
            f"https://api.prod.easypay.pt/2.0/single/{notification['id']}",
            headers={
                'AccountId': os.getenv('EASYPAY_ACCOUNT_ID'),
                'ApiKey': os.getenv('EASYPAY_API_KEY')
            }
        )

        payment = verify_response.json()

        # 3. Process the verified payment
        if payment['status'] == 'success':
            process_successful_payment(payment)
        else:
            handle_failed_payment(payment)

    except Exception as e:
        print(f"Error verifying notification: {e}")
        # Implement retry logic or alert system

    return response

def process_successful_payment(payment):
    # Your business logic here
    print(f"Processing successful payment: {payment['id']}")
    # Update database, send confirmation email, etc.

def handle_failed_payment(payment):
    # Your error handling logic
    print(f"Handling failed payment: {payment['id']}")

if __name__ == '__main__':
    app.run(port=3000)
```

## Generic Notification Fields by Payment Type

The `id` and `key` fields in generic notifications change according to payment type and operation:

| Payment Type | id | key |
|  --- | --- | --- |
| **Single Authorisation** | Single payment ID | `key` from create request |
| **Single Capture** | Single payment ID | `transaction_key` from capture request |
| **Single Sale** | Single payment ID | `transaction_key` from `capture` object in create request |
| **Frequent Create** | Frequent payment ID | `key` from create request |
| **Frequent Authorization** | Frequent payment ID | `transaction_key` from authorization request |
| **Frequent Capture** | Capture operation ID | `transaction_key` from capture request |
| **Refund** | Refund ID | `transaction_key` from refund request |
| **Void** | Void ID | `transaction_key` from void request |
| **Subscription Create** | Subscription ID | `key` from create request |
| **Subscription Capture** | Subscription ID | `transaction_key` from `capture` object |
| **Chargeback Single** | Single ID | `transaction_key` from create request |
| **Chargeback Frequent** | Capture operation ID | `transaction_key` from capture request |
| **Out Payment** | Out payment ID | `key` from create request |


## Security Best Practices

1. **Always Verify Notifications**: Query the Easypay API to confirm webhook authenticity
2. **Use HTTPS**: Ensure your webhook endpoint uses HTTPS
3. **Validate Payload**: Verify the payload structure before processing
4. **Idempotent Processing**: Handle duplicate notifications gracefully
5. **IP Whitelisting**: Optionally restrict access to Easypay's IP ranges
6. **Log Everything**: Keep detailed logs for troubleshooting
7. **Monitor Failures**: Alert on webhook processing failures


## Testing Webhooks

### Local Development with ngrok


```bash
# 1. Start your local server
npm start  # or python app.py

# 2. Expose local server with ngrok
ngrok http 3000

# 3. Use the ngrok URL in your Easypay backoffice
# Example: https://abc123.ngrok.io/webhooks/easypay/generic
```

### Manual Testing

Create test payments and monitor webhook delivery:


```bash
# Create a test payment
curl -X POST 'https://api.test.easypay.pt/2.0/single' \
  -H 'AccountId: 2b0f63e2-9fb5-4e52-aca0-b4bf0339bbe6' \
  -H 'ApiKey: eae4aa59-8e5b-4ec2-887d-b02768481a92' \
  -H 'Content-Type: application/json' \
  -d '{
    "type": "sale",
    "value": 10.00,
    "method": "cc"
  }'
```

## Monitoring Webhooks

You can monitor webhook delivery via the Easypay backoffice:

1. Log in to Easypay backoffice
2. Navigate to `Developers > Notifications API 2.0`
3. Select your payment account
4. View webhook delivery status, retry attempts, and response codes


## Handling Failures

If your webhook endpoint is unreachable or returns an error, Easypay will:

- Retry the webhook multiple times
- Use exponential backoff between retries
- Eventually mark the webhook as failed


## Next Steps

- [Payment Types](/docs/guides/payment-types) - Understand different payment workflows
- [Authorizations & Captures](/docs/guides/authorizations-captures) - Learn about auth/capture events
- [API Reference](/openapi) - Webhook payload schemas
- [Error Handling](/docs/error-handling) - Handle webhook errors gracefully