# Cardyfie API Developer Guide

## Table of Contents

1. [Introduction](#introduction)
2. [Getting Started](#getting-started)
3. [Authentication](#authentication)
4. [Base URL](#base-url)
5. [API Endpoints](#api-endpoints)
6. [Request & Response Format](#request--response-format)
7. [Error Handling](#error-handling)
8. [Code Examples](#code-examples)
9. [Best Practices](#best-practices)
10. [Rate Limits](#rate-limits)
11. [Webhooks](#webhooks)
12. [Testing](#testing)
13. [Support](#support)

---

## Introduction

Welcome to the Cardyfie API Developer Guide! This API provides a simplified interface to manage virtual cards, customers, and transactions through the Cardyfie platform. Our API abstracts the complexity of the underlying Cardyfie service, making it easier for developers to integrate virtual card functionality into their applications.

### What You Can Do

- **Customer Management**: Create, retrieve, and update customer profiles
- **Card Management**: Issue virtual cards, view card details, and manage card status
- **Financial Operations**: Deposit funds, withdraw funds, and view transaction history
- **Card Control**: Freeze, unfreeze, and close cards as needed

### Key Features

- ✅ Simplified RESTful API
- ✅ No authentication required (handled server-side)
- ✅ Comprehensive error handling
- ✅ Automatic date format normalization
- ✅ Flexible parameter handling (path or query parameters)
- ✅ Real-time transaction monitoring
- ✅ Webhook support for event notifications

---

## Getting Started

### Prerequisites

- HTTP client library for your programming language
- Understanding of RESTful APIs and JSON
- Webhook endpoint URL (optional, for receiving events)

### Quick Start

1. **Set Base URL**: Use `https://debit.gmpayapp.site/public/api/cardyfie` as your base URL
2. **Make Your First Request**: Try the health check endpoint to verify connectivity

```bash
curl -X GET "https://debit.gmpayapp.site/public/api/cardyfie/health"
```

---

## Authentication

**No authentication required!** All API endpoints are publicly accessible. Authentication is handled server-side using our configured Cardyfie API credentials.

Simply make requests to the endpoints without any authentication headers.

---

## Base URL

All API endpoints are relative to this base URL:

```
https://debit.gmpayapp.site/public/api/cardyfie
```

### Environment

- **Production**: `https://debit.gmpayapp.site/public/api/cardyfie`
- **Sandbox**: Same URL (environment is configured server-side)

---

## API Endpoints

### Health Check

Check if the API is operational.

**Endpoint**: `GET /health`

**Response**:
```json
{
    "success": true,
    "message": "Cardyfie service is running",
    "environment": "sandbox",
    "timestamp": "2025-01-15 12:00:00"
}
```

---

### Customer Endpoints

#### Create Customer

Create a new customer profile in Cardyfie.

**Endpoint**: `POST /customer/create`

**Request Body**:
```json
{
    "first_name": "John",
    "last_name": "Doe",
    "email": "[email protected]",
    "date_of_birth": "1990-12-10",
    "id_type": "passport",
    "id_number": "GBR123456789",
    "id_front_image": "https://example.com/id_front.jpg",
    "user_image": "https://example.com/user.jpg",
    "house_number": "221B",
    "address_line_1": "Baker Street",
    "city": "London",
    "zip_code": "NW1 6XE",
    "country": "UK"
}
```

**Required Fields**:
- `first_name` - Customer's first name
- `last_name` - Customer's last name
- `email` - Valid email address
- `date_of_birth` - Date in `YYYY-MM-DD` or `DD/MM/YYYY` format
- `id_type` - One of: `passport`, `nid`, `bvn`
- `id_number` - ID document number
- `id_front_image` - Public HTTPS URL to front of ID image
- `user_image` - Public HTTPS URL to customer photo
- `house_number` - House/building number
- `address_line_1` - Street address
- `city` - City name
- `zip_code` - Postal/ZIP code
- `country` - Country code (e.g., "UK", "UG")

**Optional Fields**:
- `id_back_image` - URL to back of ID image
- `state` - State/Province name
- `reference_id` - Your custom reference (auto-generated if not provided)
- `user_id` - Your internal user ID (stored in meta)

**Success Response** (200):
```json
{
    "success": true,
    "message": "Customer created successfully!",
    "customer": {
        "ulid": "01K44CWWXSAAHPSCQK8TP4W7D0",
        "first_name": "John",
        "last_name": "Doe",
        "email": "[email protected]",
        "status": "PENDING",
        "reference_id": "CF_1234567890_abcd1234",
        "created_at": "2025-09-02T05:02:51.000000Z"
    },
    "ulid": "01K44CWWXSAAHPSCQK8TP4W7D0",
    "reference_id": "CF_1234567890_abcd1234"
}
```

#### Get Customer

Retrieve customer details by ULID.

**Endpoint**: `GET /customer/get/{ulid}`

**Alternative**: `GET /customer/get?ulid={ulid}`

**Success Response** (200):
```json
{
    "success": true,
    "message": "Data fetch successfully!",
    "customer": {
        "ulid": "01K44CWWXSAAHPSCQK8TP4W7D0",
        "first_name": "John",
        "last_name": "Doe",
        "email": "[email protected]",
        "status": "PENDING",
        "created_at": "2025-09-02T05:02:51.000000Z"
    }
}
```

#### Update Customer

Update an existing customer profile.

**Endpoint**: `PUT /customer/update/{ulid}`

**Note**: Email is NOT included in update (unlike create). Image fields can be omitted to keep existing images.

**Request Body**:
```json
{
    "first_name": "John",
    "last_name": "Doe",
    "date_of_birth": "1990-12-10",
    "id_type": "passport",
    "id_number": "GBR123456789",
    "id_front_image": "https://example.com/id_front.jpg",
    "user_image": "https://example.com/user.jpg",
    "house_number": "221B",
    "address_line_1": "Baker Street",
    "city": "London",
    "zip_code": "NW1 6XE"
}
```

---

### Card Endpoints

#### Get Card Currencies

Retrieve list of available currencies for card issuance.

**Endpoint**: `GET /card/currencies`

**Success Response** (200):
```json
{
    "success": true,
    "message": "Data fetch successfully!",
    "currencies": [
        {
            "id": 2,
            "currency": {
                "code": "USD",
                "name": "United States dollar",
                "symbol": "$"
            },
            "status": "ACTIVE"
        }
    ],
    "count": 1
}
```

#### Issue Card

Create a virtual card for a customer.

**Endpoint**: `POST /card/issue`

**Request Body**:
```json
{
    "customer_ulid": "01K44CWWXSAAHPSCQK8TP4W7D0",
    "card_name": "John Doe",
    "card_currency": "USD",
    "card_type": "universal",
    "card_provider": "visa",
    "reference_id": "CARD_REF_12345"
}
```

**Required Fields**:
- `customer_ulid` - Customer ULID from create customer response
- `card_name` - Name to be printed on the card
- `card_currency` - Currency code (e.g., "USD", "EUR")
- `card_type` - Card type: `universal` or `platinum`
- `card_provider` - Card provider: `visa` or `mastercard`

**Success Response** (200):
```json
{
    "success": true,
    "message": "Card Issued Successfully!",
    "card": {
        "ulid": "01K62R43VAMPRMBDNT2W76RNDV",
        "card_name": "John Doe",
        "card_balance": "0.000000000000000000",
        "card_currency_code": "USD",
        "card_type": "universal",
        "card_provider": "visa",
        "masked_pan": "4334 51****** 4713",
        "status": "ENABLED"
    },
    "card_ulid": "01K62R43VAMPRMBDNT2W76RNDV"
}
```

#### Get All Cards

Retrieve paginated list of all cards.

**Endpoint**: `GET /card/get-all`

**Query Parameters**:
- `page` (optional) - Page number (default: 1)
- `per_page` (optional) - Items per page (default: 10, max: 100)

**Example**: `GET /card/get-all?page=1&per_page=20`

**Success Response** (200):
```json
{
    "success": true,
    "message": "Data fetch successfully!",
    "cards": [...],
    "pagination": {
        "current_page": 1,
        "per_page": 10,
        "total": 3,
        "last_page": 1
    },
    "count": 3
}
```

#### Get Card Details

Retrieve detailed information about a specific card, including sensitive data (CVV, real PAN).

**Endpoint**: `GET /card/details/{ulid}`

**Alternative**: `GET /card/details?ulid={ulid}`

**⚠️ Security Note**: This endpoint returns sensitive information (CVV and real PAN). Handle with care and never log or expose this data.

**Success Response** (200):
```json
{
    "success": true,
    "message": "Virtual Card Details Fetch Successfully!",
    "card": {
        "ulid": "01K5GXP0RQ5KYECGBJE7Z4AJNG",
        "card_name": "John Doe",
        "card_balance": "100.000000000000000000",
        "card_currency_code": "USD",
        "masked_pan": "4334 51****** 1140",
        "real_pan": "4334 5139 4939 1140",
        "cvv": "450",
        "status": "ENABLED"
    }
}
```

#### Deposit to Card

Add funds to a card balance.

**Endpoint**: `POST /card/deposit/{ulid}`

**Alternative**: `POST /card/deposit` (with `ulid` in body)

**Request Body**:
```json
{
    "amount": 100.50
}
```

**Required Fields**:
- `amount` - Amount to deposit (can be a number or string, greater than zero)

**Note**: You can send the amount as a number (e.g., `100.50`) or string (e.g., `"100.50"`). The API automatically converts it to the string format required by Cardyfie.

**Success Response** (200):
```json
{
    "success": true,
    "message": "Card Deposited Successfully!",
    "card_ulid": "01K5GXP0RQ5KYECGBJE7Z4AJNG",
    "amount": 100.50
}
```

#### Withdraw from Card

Remove funds from a card balance.

**Endpoint**: `POST /card/withdraw/{ulid}`

**Request Body**:
```json
{
    "amount": 50.25
}
```

**Required Fields**:
- `amount` - Amount to withdraw (can be a number or string, greater than zero)

**Note**: You can send the amount as a number (e.g., `50.25`) or string (e.g., `"50.25"`). The API automatically converts it to the string format required by Cardyfie.

#### Get Card Transactions

Retrieve transactions for a specific card or all cards.

**Endpoint**: `GET /card/transactions`

**Query Parameters**:
- `card_ulid` (optional) - Filter transactions for a specific card

**Example**: `GET /card/transactions?card_ulid=01K5V21E7MED7EG2K9612XPEK4`

**Success Response** (200):
```json
{
    "success": true,
    "message": "Data fetch successfully!",
    "transactions": [
        {
            "ulid": "01K5V21E7MED7EG2K9612XPEK4",
            "trx_type": "CARD-ISSUE",
            "trx_id": "VCI-rxgNBWCWln5FH1",
            "card_currency": "USD",
            "enter_amount": "0.000000000000000000",
            "total_payable": "3.000000000000000000",
            "fees": "3.000000000000000000",
            "amount_type": "CREDIT",
            "status": "SUCCESS",
            "created_at": "2025-09-23T10:31:20.000000Z"
        }
    ],
    "count": 1
}
```

#### Freeze Card

Freeze a card, preventing it from being used for transactions.

**Endpoint**: `POST /card/freeze/{ulid}`

**Request Body**: Empty `{}`

**Success Response** (200):
```json
{
    "success": true,
    "message": "Card freeze successfully!",
    "card_ulid": "01K5GXP0RQ5KYECGBJE7Z4AJNG"
}
```

#### Unfreeze Card

Unfreeze a card, allowing it to be used again.

**Endpoint**: `POST /card/unfreeze/{ulid}`

**Request Body**: Empty `{}`

#### Close Card

Permanently close a card. **⚠️ This action is irreversible.**

**Endpoint**: `POST /card/close/{ulid}`

**Request Body**: Empty `{}`

**Success Response** (200):
```json
{
    "success": true,
    "message": "Card closed successfully!",
    "card_ulid": "01K5GXP0RQ5KYECGBJE7Z4AJNG"
}
```

---

## Request & Response Format

### Request Format

- **Content-Type**: `application/json`
- **Method**: GET, POST, PUT (as specified per endpoint)
- **Headers**: 
  ```
  Content-Type: application/json
  Accept: application/json
  ```
  
**Note**: No authentication headers required - authentication is handled server-side.

### Response Format

All responses follow this structure:

**Success Response**:
```json
{
    "success": true,
    "message": "Operation successful message",
    "data": { ... }
}
```

**Error Response**:
```json
{
    "success": false,
    "message": "Error description",
    "error_code": 400
}
```

### HTTP Status Codes

- `200` - Success
- `400` - Bad Request (validation errors, missing fields)
- `404` - Not Found (resource doesn't exist)
- `500` - Internal Server Error

---

## Error Handling

### Common Error Scenarios

#### 1. Missing Required Fields

**Request**:
```json
{
    "first_name": "John"
    // Missing other required fields
}
```

**Response** (400):
```json
{
    "success": false,
    "message": "Missing required field: last_name",
    "error_code": 400
}
```

#### 2. Resource Not Found

**Response** (404):
```json
{
    "success": false,
    "message": "Customer not found",
    "error_code": 404
}
```

### Error Handling Best Practices

1. **Always check the `success` field** before processing response data
2. **Handle specific error codes** appropriately in your application
3. **Log error messages** for debugging (but never log sensitive data)
4. **Implement retry logic** for transient errors (5xx status codes)
5. **Display user-friendly messages** based on error types

### Example Error Handling

```javascript
async function createCustomer(customerData) {
    try {
        const response = await fetch('https://debit.gmpayapp.site/public/api/cardyfie/customer/create', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify(customerData)
        });
        
        const data = await response.json();
        
        if (!data.success) {
            // Handle specific error cases
            if (response.status === 400) {
                throw new Error(`Validation Error: ${data.message}`);
            } else {
                throw new Error(data.message || 'An error occurred');
            }
        }
        
        return data;
    } catch (error) {
        console.error('Error creating customer:', error);
        throw error;
    }
}
```

---

## Code Examples

### JavaScript/Node.js

#### Using Fetch API

```javascript
const API_BASE_URL = 'https://debit.gmpayapp.site/public/api/cardyfie';

// Helper function for API calls
async function apiCall(endpoint, method = 'GET', body = null) {
    const options = {
        method,
        headers: {
            'Content-Type': 'application/json',
            'Accept': 'application/json'
        }
    };
    
    if (body) {
        options.body = JSON.stringify(body);
    }
    
    const response = await fetch(`${API_BASE_URL}${endpoint}`, options);
    const data = await response.json();
    
    if (!data.success) {
        throw new Error(data.message || 'API request failed');
    }
    
    return data;
}

// Create Customer
async function createCustomer(customerData) {
    return await apiCall('/customer/create', 'POST', customerData);
}

// Issue Card
async function issueCard(cardData) {
    return await apiCall('/card/issue', 'POST', cardData);
}

// Get Card Details
async function getCardDetails(cardUlid) {
    return await apiCall(`/card/details/${cardUlid}`);
}

// Deposit to Card
async function depositToCard(cardUlid, amount) {
    return await apiCall(`/card/deposit/${cardUlid}`, 'POST', { amount });
}

// Get Transactions
async function getTransactions(cardUlid = null) {
    const endpoint = cardUlid 
        ? `/card/transactions?card_ulid=${cardUlid}`
        : '/card/transactions';
    return await apiCall(endpoint);
}

// Usage Example
(async () => {
    try {
        // Create customer
        const customer = await createCustomer({
            first_name: "John",
            last_name: "Doe",
            email: "[email protected]",
            date_of_birth: "1990-12-10",
            id_type: "passport",
            id_number: "GBR123456789",
            id_front_image: "https://example.com/id_front.jpg",
            user_image: "https://example.com/user.jpg",
            house_number: "221B",
            address_line_1: "Baker Street",
            city: "London",
            zip_code: "NW1 6XE",
            country: "UK"
        });
        
        console.log('Customer created:', customer.customer.ulid);
        
        // Issue card
        const card = await issueCard({
            customer_ulid: customer.customer.ulid,
            card_name: "John Doe",
            card_currency: "USD",
            card_type: "universal",
            card_provider: "visa"
        });
        
        console.log('Card issued:', card.card.ulid);
        
        // Deposit funds
        await depositToCard(card.card.ulid, 100.50);
        console.log('Deposit successful');
        
        // Get transactions
        const transactions = await getTransactions(card.card.ulid);
        console.log('Transactions:', transactions.transactions);
        
    } catch (error) {
        console.error('Error:', error.message);
    }
})();
```

#### Using Axios

```javascript
const axios = require('axios');

const api = axios.create({
    baseURL: 'https://debit.gmpayapp.site/public/api/cardyfie',
    headers: {
        'Content-Type': 'application/json'
    }
});

// Create customer
const createCustomer = async (customerData) => {
    const response = await api.post('/customer/create', customerData);
    return response.data;
};

// Issue card
const issueCard = async (cardData) => {
    const response = await api.post('/card/issue', cardData);
    return response.data;
};
```

### Python

#### Using Requests Library

```python
import requests
import json

API_BASE_URL = 'https://debit.gmpayapp.site/public/api/cardyfie'

headers = {
    'Content-Type': 'application/json',
    'Accept': 'application/json'
}

def api_call(endpoint, method='GET', data=None):
    """Helper function for API calls"""
    url = f'{API_BASE_URL}{endpoint}'
    
    if method == 'GET':
        response = requests.get(url, headers=headers)
    elif method == 'POST':
        response = requests.post(url, headers=headers, json=data)
    elif method == 'PUT':
        response = requests.put(url, headers=headers, json=data)
    
    response.raise_for_status()
    result = response.json()
    
    if not result.get('success'):
        raise Exception(result.get('message', 'API request failed'))
    
    return result

# Create Customer
def create_customer(customer_data):
    return api_call('/customer/create', 'POST', customer_data)

# Issue Card
def issue_card(card_data):
    return api_call('/card/issue', 'POST', card_data)

# Get Card Details
def get_card_details(card_ulid):
    return api_call(f'/card/details/{card_ulid}')

# Deposit to Card
def deposit_to_card(card_ulid, amount):
    return api_call(f'/card/deposit/{card_ulid}', 'POST', {'amount': amount})

# Get Transactions
def get_transactions(card_ulid=None):
    endpoint = f'/card/transactions?card_ulid={card_ulid}' if card_ulid else '/card/transactions'
    return api_call(endpoint)

# Usage Example
if __name__ == '__main__':
    try:
        # Create customer
        customer = create_customer({
            'first_name': 'John',
            'last_name': 'Doe',
            'email': '[email protected]',
            'date_of_birth': '1990-12-10',
            'id_type': 'passport',
            'id_number': 'GBR123456789',
            'id_front_image': 'https://example.com/id_front.jpg',
            'user_image': 'https://example.com/user.jpg',
            'house_number': '221B',
            'address_line_1': 'Baker Street',
            'city': 'London',
            'zip_code': 'NW1 6XE',
            'country': 'UK'
        })
        
        print(f"Customer created: {customer['customer']['ulid']}")
        
        # Issue card
        card = issue_card({
            'customer_ulid': customer['customer']['ulid'],
            'card_name': 'John Doe',
            'card_currency': 'USD',
            'card_type': 'universal',
            'card_provider': 'visa'
        })
        
        print(f"Card issued: {card['card']['ulid']}")
        
        # Deposit funds
        deposit_result = deposit_to_card(card['card']['ulid'], 100.50)
        print("Deposit successful")
        
        # Get transactions
        transactions = get_transactions(card['card']['ulid'])
        print(f"Transactions: {len(transactions['transactions'])} found")
        
    except Exception as e:
        print(f"Error: {e}")
```

### PHP

```php
<?php

class CardyfieAPI {
    private $baseUrl = 'https://debit.gmpayapp.site/public/api/cardyfie';
    
    public function __construct() {
        // No API key needed - authentication handled server-side
    }
    
    private function request($endpoint, $method = 'GET', $data = null) {
        $url = $this->baseUrl . $endpoint;
        
        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
            'Content-Type: application/json',
            'Accept: application/json'
        ]);
        
        if ($method === 'POST') {
            curl_setopt($ch, CURLOPT_POST, true);
            if ($data) {
                curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
            }
        } elseif ($method === 'PUT') {
            curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
            if ($data) {
                curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
            }
        }
        
        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);
        
        $result = json_decode($response, true);
        
        if ($httpCode >= 400 || !$result['success']) {
            throw new Exception($result['message'] ?? 'API request failed');
        }
        
        return $result;
    }
    
    public function createCustomer($customerData) {
        return $this->request('/customer/create', 'POST', $customerData);
    }
    
    public function issueCard($cardData) {
        return $this->request('/card/issue', 'POST', $cardData);
    }
    
    public function getCardDetails($cardUlid) {
        return $this->request("/card/details/{$cardUlid}");
    }
    
    public function depositToCard($cardUlid, $amount) {
        return $this->request("/card/deposit/{$cardUlid}", 'POST', ['amount' => $amount]);
    }
    
    public function getTransactions($cardUlid = null) {
        $endpoint = $cardUlid 
            ? "/card/transactions?card_ulid={$cardUlid}"
            : '/card/transactions';
        return $this->request($endpoint);
    }
}

// Usage Example
try {
    $api = new CardyfieAPI();
    
    // Create customer
    $customer = $api->createCustomer([
        'first_name' => 'John',
        'last_name' => 'Doe',
        'email' => '[email protected]',
        'date_of_birth' => '1990-12-10',
        'id_type' => 'passport',
        'id_number' => 'GBR123456789',
        'id_front_image' => 'https://example.com/id_front.jpg',
        'user_image' => 'https://example.com/user.jpg',
        'house_number' => '221B',
        'address_line_1' => 'Baker Street',
        'city' => 'London',
        'zip_code' => 'NW1 6XE',
        'country' => 'UK'
    ]);
    
    echo "Customer created: " . $customer['customer']['ulid'] . "\n";
    
    // Issue card
    $card = $api->issueCard([
        'customer_ulid' => $customer['customer']['ulid'],
        'card_name' => 'John Doe',
        'card_currency' => 'USD',
        'card_type' => 'universal',
        'card_provider' => 'visa'
    ]);
    
    echo "Card issued: " . $card['card']['ulid'] . "\n";
    
} catch (Exception $e) {
    echo "Error: " . $e->getMessage() . "\n";
}
?>
```

### cURL Examples

#### Create Customer
```bash
curl -X POST "https://debit.gmpayapp.site/public/api/cardyfie/customer/create" \
  -H "Content-Type: application/json" \
  -d '{
    "first_name": "John",
    "last_name": "Doe",
    "email": "[email protected]",
    "date_of_birth": "1990-12-10",
    "id_type": "passport",
    "id_number": "GBR123456789",
    "id_front_image": "https://example.com/id_front.jpg",
    "user_image": "https://example.com/user.jpg",
    "house_number": "221B",
    "address_line_1": "Baker Street",
    "city": "London",
    "zip_code": "NW1 6XE",
    "country": "UK"
  }'
```

#### Issue Card
```bash
curl -X POST "https://debit.gmpayapp.site/public/api/cardyfie/card/issue" \
  -H "Content-Type: application/json" \
  -d '{
    "customer_ulid": "01K44CWWXSAAHPSCQK8TP4W7D0",
    "card_name": "John Doe",
    "card_currency": "USD",
    "card_type": "universal",
    "card_provider": "visa"
  }'
```

#### Deposit to Card
```bash
curl -X POST "https://debit.gmpayapp.site/public/api/cardyfie/card/deposit/01K5GXP0RQ5KYECGBJE7Z4AJNG" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 100.50
  }'
```

---

## Best Practices

### 1. Security

- **No API keys needed** - Authentication is handled server-side
- **Implement HTTPS only** for all API communications
- **Never log sensitive data** (CVV, real PAN, card numbers)
- **Validate and sanitize** all user inputs before sending to API
- **Verify webhook signatures** if you're receiving webhooks from Cardyfie

### 2. Error Handling

- **Always check the `success` field** in responses
- **Implement proper error handling** for all API calls
- **Use try-catch blocks** to handle exceptions
- **Log errors** for debugging (without sensitive data)
- **Provide user-friendly error messages**

### 3. Performance

- **Cache card details** when possible (but refresh sensitive data like balance)
- **Use pagination** for large data sets
- **Implement request timeouts** (recommended: 30 seconds)
- **Batch operations** when possible
- **Monitor API response times**

### 4. Data Management

- **Store ULIDs** for customers and cards in your database
- **Keep reference IDs** for tracking your own transactions
- **Sync card balances** periodically if needed
- **Handle date formats** consistently (API accepts multiple formats)

### 5. Testing

- **Test in sandbox** before production
- **Test error scenarios** (invalid data, missing fields, etc.)
- **Test edge cases** (zero amounts, very large amounts, etc.)
- **Implement unit tests** for API integration code

### 6. Code Organization

```javascript
// Recommended structure
class CardyfieService {
    constructor() {
        this.baseUrl = 'https://debit.gmpayapp.site/public/api/cardyfie';
        // No API key needed - authentication handled server-side
    }
    
    // Customer methods
    async createCustomer(data) { ... }
    async getCustomer(ulid) { ... }
    async updateCustomer(ulid, data) { ... }
    
    // Card methods
    async issueCard(data) { ... }
    async getCardDetails(ulid) { ... }
    async depositToCard(ulid, amount) { ... }
    async withdrawFromCard(ulid, amount) { ... }
    async freezeCard(ulid) { ... }
    async unfreezeCard(ulid) { ... }
    async closeCard(ulid) { ... }
    
    // Transaction methods
    async getTransactions(cardUlid = null) { ... }
}
```

---

## Rate Limits

Currently, there are no strict rate limits enforced. However, we recommend:

- **Maximum 100 requests per minute** per client
- **Implement exponential backoff** for retries
- **Cache responses** when appropriate
- **Contact support** if you need higher limits

If you exceed recommended limits, you may receive a `429 Too Many Requests` response.

---

## Webhooks

Webhooks allow you to receive real-time notifications when events occur in the Cardyfie system. Instead of polling the API, Cardyfie will send HTTP POST requests to your webhook URL when events happen.

### Setting Up Webhooks

To receive webhooks from Cardyfie:

1. **Login to your Cardyfie account** at [https://cardyfie.com/login](https://cardyfie.com/login)
2. **Navigate to API Credentials** page under developer section
3. **Create an App** and enter your app name
4. **Click on webhook action button** in your app
5. **Enter your webhook URL** and update
6. **Copy your Secret Key** for signature verification

### Webhook Endpoint

**Endpoint**: `POST /api/cardyfie/webhook`

**Full URL**: `https://debit.gmpayapp.site/public/api/cardyfie/webhook`

**Important**: This endpoint is for **Cardyfie to call**, not for you to call. When configuring webhooks in the Cardyfie dashboard, use this URL as your webhook endpoint. The system will automatically:
- Verify webhook signatures (if configured)
- Log webhook events
- Forward webhooks to additional URLs (if configured in environment variables)

### Webhook Events

Cardyfie sends webhooks for the following events:

#### 1. Customer Created (`customer.create`)

Triggered when a customer is created and approved.

**Webhook Payload**:
```json
{
    "event": {
        "id": "01K627KVEG0MRV1NJ3ST3V7QB3",
        "type": "customer.create"
    },
    "data": {
        "first_name": "John",
        "last_name": "Doe",
        "email": "[email protected]",
        "date_of_birth": "10/12/1990",
        "id_type": "passport",
        "id_number": "6464649416311",
        "ulid": "01K44CWWXSAAHPSCQK8TP4W7D0",
        "status": "APPROVED",
        "created_at": "2025-09-23T10:29:28.000000Z"
    }
}
```

#### 2. Card Issued (`card.issued`)

Triggered when a virtual card is successfully issued.

**Webhook Payload**:
```json
{
    "event": {
        "id": "01K62YD00C0VX35M42SS0SVMXM",
        "type": "card.issued"
    },
    "data": {
        "id": 4,
        "ulid": "01K62YCZZPD5T1HVNMGY3E88EE",
        "card_name": "John Doe",
        "card_balance": "0.000000000000000000",
        "card_currency_code": "USD",
        "card_type": "universal",
        "card_provider": "visa",
        "card_exp_time": "09/2027",
        "masked_pan": "4334 51****** 9529",
        "status": "ENABLED",
        "created_at": "2025-09-26T12:01:39.000000Z"
    }
}
```

### Webhook Signature Verification

To ensure webhooks are authentic and from Cardyfie, verify the signature using the `x-webhook-signature` header.

#### Signature Verification Algorithm

The signature is an HMAC SHA256 hash of the JSON payload using your webhook secret key.

**JavaScript Example**:
```javascript
const crypto = require('crypto');

function verifyWebhookSignature(payload, receivedSignature, secret) {
    const expectedSignature = crypto
        .createHmac('sha256', secret)
        .update(JSON.stringify(payload))
        .digest('hex');
    
    return crypto.timingSafeEqual(
        Buffer.from(expectedSignature),
        Buffer.from(receivedSignature)
    );
}

// Usage
app.post('/webhook', (req, res) => {
    const signature = req.headers['x-webhook-signature'];
    const secret = 'your_webhook_secret_key';
    const payload = req.body;
    
    if (verifyWebhookSignature(payload, signature, secret)) {
        // Webhook is authentic, process it
        console.log('Webhook verified:', payload.event.type);
        res.status(200).send('OK');
    } else {
        // Invalid signature, reject
        res.status(401).send('Invalid signature');
    }
});
```

**PHP Example**:
```php
function verify_webhook_signature($payload, $received_signature, $secret) {
    $expected_signature = hash_hmac('sha256', json_encode($payload), $secret);
    return hash_equals($expected_signature, $received_signature);
}

// Usage
$payload = json_decode(file_get_contents('php://input'), true);
$signature = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? '';
$secret = 'your_webhook_secret_key';

if (verify_webhook_signature($payload, $signature, $secret)) {
    // Webhook is authentic
    http_response_code(200);
    echo 'OK';
} else {
    // Invalid signature
    http_response_code(401);
    echo 'Invalid signature';
}
```

**Python Example**:
```python
import hmac
import hashlib
import json

def verify_webhook_signature(payload, received_signature, secret):
    expected_signature = hmac.new(
        secret.encode('utf-8'),
        json.dumps(payload, separators=(',', ':')).encode('utf-8'),
        hashlib.sha256
    ).hexdigest()
    
    return hmac.compare_digest(expected_signature, received_signature)

# Usage in Flask
@app.route('/webhook', methods=['POST'])
def webhook():
    signature = request.headers.get('X-Webhook-Signature')
    secret = 'your_webhook_secret_key'
    payload = request.json
    
    if verify_webhook_signature(payload, signature, secret):
        # Process webhook
        print(f"Webhook verified: {payload['event']['type']}")
        return 'OK', 200
    else:
        return 'Invalid signature', 401
```

### Webhook Best Practices

1. **Always verify signatures** - Never process webhooks without signature verification
2. **Respond quickly** - Return 200 OK within 5 seconds to acknowledge receipt
3. **Idempotency** - Handle duplicate webhooks gracefully (check event ID)
4. **Log webhooks** - Log all received webhooks for debugging
5. **Error handling** - Implement retry logic for failed webhook processing
6. **HTTPS only** - Always use HTTPS for webhook endpoints

### Webhook Response

Your webhook endpoint should return:

- **200 OK** - Webhook received and processed successfully
- **4xx/5xx** - Cardyfie may retry the webhook

### Testing Webhooks

You can test your webhook endpoint using tools like:

- **ngrok** - Expose local server for testing
- **Postman** - Send test webhook payloads
- **Cardyfie Dashboard** - Trigger test webhooks from the dashboard

### Example Webhook Handler

```javascript
// Express.js example
const express = require('express');
const crypto = require('crypto');
const app = express();

app.use(express.json());

app.post('/webhook', (req, res) => {
    const signature = req.headers['x-webhook-signature'];
    const secret = process.env.CARDYFIE_WEBHOOK_SECRET;
    const payload = req.body;
    
    // Verify signature
    if (!verifyWebhookSignature(payload, signature, secret)) {
        return res.status(401).send('Invalid signature');
    }
    
    // Process webhook based on event type
    const eventType = payload.event.type;
    
    switch (eventType) {
        case 'customer.create':
            handleCustomerCreated(payload.data);
            break;
        case 'card.issued':
            handleCardIssued(payload.data);
            break;
        default:
            console.log('Unknown event type:', eventType);
    }
    
    // Always return 200 OK
    res.status(200).send('OK');
});

function handleCustomerCreated(customerData) {
    console.log('Customer created:', customerData.ulid);
    // Update your database, send notifications, etc.
}

function handleCardIssued(cardData) {
    console.log('Card issued:', cardData.ulid);
    // Update your database, notify user, etc.
}

app.listen(3000, () => {
    console.log('Webhook server listening on port 3000');
});
```

---

## Testing

### Test Endpoints

Use the health check endpoint to verify connectivity:

```bash
curl -X GET "https://debit.gmpayapp.site/public/api/cardyfie/health"
```

### Test Data

When testing, use:
- **Sandbox environment** (configured server-side)
- **Test card numbers** provided by Cardyfie
- **Small amounts** for deposits/withdrawals

### Testing Checklist

- [ ] Health check endpoint works
- [ ] Create customer succeeds
- [ ] Get customer returns correct data
- [ ] Issue card succeeds
- [ ] Get card details returns correct data
- [ ] Deposit/withdraw operations work
- [ ] Freeze/unfreeze operations work
- [ ] Transaction history is accurate
- [ ] Error handling works correctly

---

## Support

### Getting Help

- **Documentation**: Check this guide and `CARDYFIE_SETUP.md`
- **API Health**: Use `/health` endpoint to check API status
- **Contact**: Reach out to your administrator for support

### Common Issues

**Issue**: "Missing required field" error
- **Solution**: Check that all required fields are included in your request body

**Issue**: "Customer not found" error
- **Solution**: Verify the ULID is correct and the customer exists

**Issue**: Slow API responses
- **Solution**: Some endpoints (like price lists) may take 30+ seconds. Implement appropriate timeouts.

---

## Changelog

### Version 1.0.0 (Current)
- Initial release
- Customer management endpoints
- Card management endpoints
- Transaction endpoints
- Card control endpoints (freeze, unfreeze, close)

---

## License

This API is provided as-is. Please refer to your service agreement for terms of use.

---

**Happy Coding! 🚀**

For questions or issues, please contact your administrator or refer to the main documentation.

