# Integration Examples

## Complete integration examples

### JavaScript/Node.js

Complete integration example using Express:

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

const app = express();
const PORT = process.env.PORT || 3000;

// Joy API client setup
const joyApi = axios.create({
  baseURL: 'https://joy.avada.io/app/api/v1',
  headers: {
    'X-Joy-Loyalty-App-Key': process.env.JOY_APP_KEY,
    'X-Joy-Loyalty-Secret-Key': process.env.JOY_SECRET_KEY
  }
});

// HMAC verification middleware
function verifyWebhook(req, res, next) {
  const hmac = req.get('X-Joy-Loyalty-Hmac-Sha256');
  const calculatedHmac = crypto
    .createHmac('sha256', process.env.JOY_SECRET_KEY)
    .update(req.rawBody, 'utf8')
    .digest('base64');

  if (!crypto.timingSafeEqual(Buffer.from(calculatedHmac), Buffer.from(hmac))) {
    return res.status(401).send('Invalid signature');
  }
  
  next();
}

// Raw body parser for HMAC verification
app.use('/webhook', express.raw({type: 'application/json'}), (req, res, next) => {
  req.rawBody = req.body;
  req.body = JSON.parse(req.body);
  next();
});

// Webhook handlers
app.post('/webhook/points-earned', verifyWebhook, (req, res) => {
  const {customer, oldPoint, newPoint} = req.body;
  console.log(`Customer ${customer.email} earned ${newPoint - oldPoint} points`);
  
  // Your business logic here
  
  res.status(200).send('OK');
});

app.post('/webhook/tier-upgraded', verifyWebhook, (req, res) => {
  const {customer, oldTierName, newTierName} = req.body;
  console.log(`Customer ${customer.email} upgraded from ${oldTierName} to ${newTierName}`);
  
  // Your business logic here
  
  res.status(200).send('OK');
});

// API management functions
async function createWebhook(topic, url) {
  try {
    const response = await joyApi.post('/webhooks', {topic, url});
    return response.data;
  } catch (error) {
    console.error('Failed to create webhook:', error.response?.data || error.message);
    throw error;
  }
}

async function listWebhooks() {
  try {
    const response = await joyApi.get('/webhooks');
    return response.data.webhooks;
  } catch (error) {
    console.error('Failed to list webhooks:', error.response?.data || error.message);
    throw error;
  }
}

app.listen(PORT, () => {
  console.log(`Webhook server running on port ${PORT}`);
});
```

### Python

Complete integration example using Flask:

```python
import os
import hmac
import hashlib
import base64
import json
import requests
from flask import Flask, request

app = Flask(__name__)

class JoyWebhookClient:
    def __init__(self, app_key, secret_key):
        self.app_key = app_key
        self.secret_key = secret_key
        self.base_url = 'https://joy.avada.io/app/api/v1'
        self.session = requests.Session()
        self.session.headers.update({
            'Content-Type': 'application/json',
            'X-Joy-Loyalty-App-Key': app_key,
            'X-Joy-Loyalty-Secret-Key': secret_key
        })

    def verify_webhook(self, raw_body, hmac_header):
        calculated_hmac = base64.b64encode(
            hmac.new(
                self.secret_key.encode('utf-8'),
                raw_body,
                hashlib.sha256
            ).digest()
        ).decode()
        
        return hmac.compare_digest(calculated_hmac, hmac_header)

    def create_webhook(self, topic, url):
        response = self.session.post(f'{self.base_url}/webhooks', 
                                   json={'topic': topic, 'url': url})
        if response.status_code == 200:
            return response.json()
        else:
            raise Exception(f'Failed to create webhook: {response.text}')

    def list_webhooks(self):
        response = self.session.get(f'{self.base_url}/webhooks')
        if response.status_code == 200:
            return response.json().get('webhooks', [])
        else:
            raise Exception(f'Failed to list webhooks: {response.text}')

# Initialize client
webhook_client = JoyWebhookClient(
    os.getenv('JOY_APP_KEY'),
    os.getenv('JOY_SECRET_KEY')
)

@app.route('/webhook/points-earned', methods=['POST'])
def handle_points_earned():
    # Verify HMAC
    hmac_header = request.headers.get('X-Joy-Loyalty-Hmac-Sha256')
    if not webhook_client.verify_webhook(request.data, hmac_header):
        return 'Invalid signature', 401
    
    # Process webhook
    payload = request.get_json()
    customer = payload.get('customer', {})
    old_point = payload.get('oldPoint', 0)
    new_point = payload.get('newPoint', 0)
    
    print(f"Customer {customer.get('email')} earned {new_point - old_point} points")
    
    # Your business logic here
    
    return 'OK', 200

@app.route('/webhook/tier-upgraded', methods=['POST'])
def handle_tier_upgraded():
    # Verify HMAC
    hmac_header = request.headers.get('X-Joy-Loyalty-Hmac-Sha256')
    if not webhook_client.verify_webhook(request.data, hmac_header):
        return 'Invalid signature', 401
    
    # Process webhook
    payload = request.get_json()
    customer = payload.get('customer', {})
    old_tier = payload.get('oldTierName')
    new_tier = payload.get('newTierName')
    
    print(f"Customer {customer.get('email')} upgraded from {old_tier} to {new_tier}")
    
    # Your business logic here
    
    return 'OK', 200

if __name__ == '__main__':
    app.run(debug=True, port=5000)
```

### PHP

Complete integration example:

```php
<?php
class JoyWebhookClient {
    private $appKey;
    private $secretKey;
    private $baseUrl = 'https://joy.avada.io/app/api/v1';

    public function __construct($appKey, $secretKey) {
        $this->appKey = $appKey;
        $this->secretKey = $secretKey;
    }

    public function verifyWebhook($rawBody, $hmacHeader) {
        $calculatedHmac = base64_encode(hash_hmac('sha256', $rawBody, $this->secretKey, true));
        return hash_equals($calculatedHmac, $hmacHeader);
    }

    public function createWebhook($topic, $url) {
        $data = json_encode(['topic' => $topic, 'url' => $url]);
        
        $context = stream_context_create([
            'http' => [
                'method' => 'POST',
                'header' => [
                    'Content-Type: application/json',
                    'X-Joy-Loyalty-App-Key: ' . $this->appKey,
                    'X-Joy-Loyalty-Secret-Key: ' . $this->secretKey
                ],
                'content' => $data
            ]
        ]);

        $response = file_get_contents($this->baseUrl . '/webhooks', false, $context);
        return json_decode($response, true);
    }

    public function listWebhooks() {
        $context = stream_context_create([
            'http' => [
                'method' => 'GET',
                'header' => [
                    'Content-Type: application/json',
                    'X-Joy-Loyalty-App-Key: ' . $this->appKey,
                    'X-Joy-Loyalty-Secret-Key: ' . $this->secretKey
                ]
            ]
        ]);

        $response = file_get_contents($this->baseUrl . '/webhooks', false, $context);
        $data = json_decode($response, true);
        return $data['webhooks'] ?? [];
    }
}

// Initialize client
$webhookClient = new JoyWebhookClient(
    $_ENV['JOY_APP_KEY'],
    $_ENV['JOY_SECRET_KEY']
);

// Handle webhook requests
$requestMethod = $_SERVER['REQUEST_METHOD'];
$requestPath = $_SERVER['REQUEST_URI'];

if ($requestMethod === 'POST' && strpos($requestPath, '/webhook/') === 0) {
    $rawBody = file_get_contents('php://input');
    $hmacHeader = $_SERVER['HTTP_X_JOY_LOYALTY_HMAC_SHA256'] ?? '';

    if (!$webhookClient->verifyWebhook($rawBody, $hmacHeader)) {
        http_response_code(401);
        exit('Invalid signature');
    }

    $payload = json_decode($rawBody, true);
    
    if (strpos($requestPath, '/webhook/points-earned') === 0) {
        $customer = $payload['customer'] ?? [];
        $oldPoint = $payload['oldPoint'] ?? 0;
        $newPoint = $payload['newPoint'] ?? 0;
        
        error_log("Customer {$customer['email']} earned " . ($newPoint - $oldPoint) . " points");
        
        // Your business logic here
        
    } elseif (strpos($requestPath, '/webhook/tier-upgraded') === 0) {
        $customer = $payload['customer'] ?? [];
        $oldTier = $payload['oldTierName'] ?? '';
        $newTier = $payload['newTierName'] ?? '';
        
        error_log("Customer {$customer['email']} upgraded from $oldTier to $newTier");
        
        // Your business logic here
    }

    http_response_code(200);
    echo 'OK';
}
?>
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://devdocs.joy.so/webhook-api/integration-examples.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
