---
title: "Ko-fi Implementation Notes"
description: "Real-world implementation notes for the Ko-fi to Paper Dynasty n8n workflow documenting deviations from the original plan, n8n community edition limitations, hardcoded product mappings, working architecture, testing results, and known limitations."
type: reference
domain: productivity
tags: [n8n, ko-fi, paper-dynasty, implementation, gotchas, lessons-learned]
---

# Ko-fi → Paper Dynasty Implementation Notes

**Date:** 2025-11-13
**Status:** Working in pddev, ready for production
**n8n Version:** Community (self-hosted)

## What We Actually Built

This document captures the real implementation vs. the original plan, including all the gotchas we encountered.

---

## Key Differences from Original Plan

### 1. No Custom Variables (Enterprise Feature)
**Original Plan:** Use n8n custom variable `KOFI_PRODUCT_MAP` for product mapping
**Actual Implementation:** Hardcoded product mapping in "Map Products" Code node

**Why:** Custom variables are enterprise-only in n8n

**Product Mapping (Hardcoded in Code Node):**
```javascript
const PRODUCT_MAP = {
  '61de350207': {  // Ko-fi product ID from URL
    name: 'Premium Pack',
    pack_type_id: 3,
    packs_per_quantity: 1
  },
  '2bdb7a4916': {
    name: 'Standard Pack',
    pack_type_id: 1,
    packs_per_quantity: 1
  },
  '3d7a4935aa': {
    name: 'In-Game Currency',
    type: 'currency',
    needs_manual_review: true  // Not implemented yet
  }
};
```

### 2. Team Lookup Split into 3 Nodes
**Original Plan:** Single "Lookup PD Team" Code node with `$http.request()`
**Actual Implementation:** 3 separate nodes

**Why:** `fetch()` and `$http` are not available in n8n Code nodes

**The 3 Nodes:**
1. **Extract Team Abbrev** (Code) - Regex extraction only
2. **Lookup Team API** (HTTP Request) - API call to PD
3. **Process Team Result** (Code) - Parse API response

### 3. Ko-fi Product Codes
**Original Plan:** Use Ko-fi's "direct_link_code" custom field
**Actual Reality:** Ko-fi doesn't have custom product codes

**Solution:** Using Ko-fi's internal product IDs from URLs:
- `https://ko-fi.com/s/61de350207` → ID is `61de350207`
- These IDs appear in webhook's `shop_items[].direct_link_code`

### 4. Webhook Domain Configuration
**Issue:** n8n showed `https://n8n.yourdomain.com` by default
**Solution:** Environment variables were set correctly, just needed n8n restart:
```bash
ssh root@10.10.0.210 "cd /opt/n8n && docker compose down && docker compose up -d"
```

### 5. API Request Body Formatting
**Issue:** Paper Dynasty API rejected stringified JSON
**Solution:** Use `{{ $json.pack_requests }}` without `JSON.stringify()` or `=` prefix

**Wrong:** `={{ JSON.stringify({"packs": $json.pack_requests}) }}`
**Right:** `{{ $json.pack_requests }}`

### 6. Discord Notifications
**Issue:** Empty body caused "Cannot send an empty message"
**Solution:** Always include `content` field even when using embeds

**Minimum working Discord message:**
```json
{
  "content": "Message text here",
  "embeds": [...]
}
```

### 7. Added Donation Support
**Extension:** Added Switch node to route by transaction type
**Routes:**
- Output 0: Shop Order → Grant Packs flow
- Output 1: Donation → Thank you notification
- Output 2: Subscription → (Future implementation)
- Fallback: Unknown types → Return 200 OK

---

## Final Working Architecture

```
Ko-fi Webhook POST
    ↓
[Webhook: Ko-fi Webhook]
Path: /kofi-pd-purchase
    ↓
[Code: Parse & Validate]
- Parse form data JSON
- Validate Ko-fi token (hardcoded)
    ↓
[Switch: Route by Type]
Value: {{ $json.type }}
    ├─ Output 0: "Shop Order"
    │   ↓
    │   [Code: Extract Team Abbrev]
    │   - Regex: /\b([A-Z]{2,4})\b/
    │   ↓
    │   [HTTP: Lookup Team API]
    │   - URL: pddev.manticorum.com/api/v2/teams?abbrev=...
    │   - Auth: Paper Dynasty API credential
    │   ↓
    │   [Code: Process Team Result]
    │   - Extract team ID and abbrev
    │   ↓
    │   [IF: Team Found?]
    │   ├─ True → [Code: Map Products]
    │   │           - Hardcoded product map
    │   │           - Create pack_requests array
    │   │           ↓
    │   │           [HTTP: Grant Packs]
    │   │           - POST /packs with array
    │   │           ↓
    │   │           [IF: API Success?]
    │   │           ├─ True → [HTTP: Discord Success]
    │   │           └─ False → [HTTP: Discord Error]
    │   │
    │   └─ False → [HTTP: Discord Manual Review]
    │
    ├─ Output 1: "Donation"
    │   ↓
    │   [HTTP: Discord Thank You]
    │   - Notification for manual thanks
    │
    └─ Fallback: Other types
        ↓
        [Return 200 OK]
```

---

## Current Configuration

### Environment
- **Testing:** pddev.manticorum.com (Paper Dynasty Dev)
- **Production:** pd.manticorum.com (not yet deployed)

### Credentials Created
1. **Paper Dynasty API** (Header Auth)
   - Header: `Authorization`
   - Value: `Bearer Tp3aO3jhYve5NJF1IqOmJTmk`

2. **Discord Webhook** (not used as credential)
   - URL hardcoded in HTTP Request nodes
   - Webhook: `1438578498108133488/MGx1zyAa1ewzVy3RqCsH50ZJUiVvT5J5Vl2jExGIkIsu6v0x8A9J3-ruRftClPEq91ej`

### Ko-fi Settings
- **Verification Token:** `44d1f957-ac15-497e-8306-4dc667de55d1`
- **Webhook URL (test):** `https://n8n.manticorum.com/webhook-test/kofi-pd-purchase`
- **Webhook URL (prod):** `https://n8n.manticorum.com/webhook/kofi-pd-purchase`

### Products Configured
| Ko-fi ID | Name | PD Pack Type | Quantity |
|----------|------|--------------|----------|
| 61de350207 | Premium Pack | 3 | 1 per qty |
| 2bdb7a4916 | Standard Pack | 1 | 1 per qty |
| 3d7a4935aa | In-Game Currency | N/A | Manual review |

---

## Testing Results

### Successful Test (2025-11-13)
- ✅ Webhook received and parsed
- ✅ Token validated
- ✅ Team "SKB" found via message extraction
- ✅ Product mapped (2bdb7a4916 → 5 standard packs)
- ✅ API call succeeded (201 Created)
- ✅ 5 packs granted to team SKB in pddev
- ✅ Discord success notification sent

### Test Command Used
```bash
curl -X POST 'https://n8n.manticorum.com/webhook-test/kofi-pd-purchase' \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  --data-urlencode 'data={
    "verification_token": "44d1f957-ac15-497e-8306-4dc667de55d1",
    "message_id": "test-001",
    "timestamp": "2025-11-13T12:00:00Z",
    "type": "Shop Order",
    "from_name": "Test Customer",
    "message": "SKB",
    "amount": "5.00",
    "email": "test@example.com",
    "currency": "USD",
    "kofi_transaction_id": "test-001",
    "shop_items": [
      {
        "direct_link_code": "2bdb7a4916",
        "variation_name": "Standard Pack",
        "quantity": 5
      }
    ],
    "discord_userid": null
  }'
```

---

## Known Limitations

### 1. No Discord User ID Support Yet
**Status:** Code is in place but untested
**Why:** Test payload used `discord_userid: null`
**Todo:** Test with real Discord-linked Ko-fi account

### 2. Currency Product Not Implemented
**Status:** Routed to manual review
**Reason:** Paper Dynasty doesn't have currency system yet
**Todo:** Implement currency granting when PD adds support

### 3. Subscription Type Not Handled
**Status:** Returns 200 OK but does nothing
**Reason:** Decided to implement shop orders and donations first
**Todo:** Add subscription tier handling

### 4. Single Discord Channel
**Status:** All notifications go to "cals-hidey-hole"
**Todo:** Consider separate channels for success/errors/reviews

### 5. Hardcoded Configuration
**Status:** All settings hardcoded in Code nodes
**Why:** Community edition limitations
**Impact:** Must edit code nodes to change products, tokens, or URLs
**Mitigation:** Well-documented code with clear comments

---

## Production Deployment Checklist

Before going live with production:

- [ ] **Update all API URLs from pddev to prod:**
  - [ ] "Lookup Team API" node URL
  - [ ] "Grant Packs" node URL

- [ ] **Update Ko-fi webhook URL:**
  - [ ] Change from `/webhook-test/` to `/webhook/`
  - [ ] Configure in Ko-fi dashboard

- [ ] **Activate workflow:**
  - [ ] Toggle "Active" switch in n8n

- [ ] **Test with real Ko-fi purchase:**
  - [ ] Create $0.01 test product
  - [ ] Make test purchase
  - [ ] Verify packs granted in production
  - [ ] Verify Discord notification

- [ ] **Monitor first 10 transactions:**
  - [ ] Watch Discord for notifications
  - [ ] Check n8n execution logs
  - [ ] Verify pack distribution accuracy

- [ ] **Document any issues encountered**

---

## Troubleshooting Guide

### Webhook Not Triggering
**Symptoms:** n8n shows "Waiting for trigger event" but nothing happens
**Solutions:**
1. Verify workflow is Active (green toggle)
2. Check webhook path matches Ko-fi configuration
3. Test with cURL to isolate issue
4. Check n8n logs: `ssh root@10.10.0.210 "cd /opt/n8n && docker compose logs -f n8n"`

### Token Validation Failing
**Symptoms:** Workflow stops at "Parse & Validate" with error
**Solutions:**
1. Verify token in Code node matches Ko-fi dashboard exactly
2. Check for extra spaces or quotes
3. Re-copy token from Ko-fi

### Team Not Found
**Symptoms:** Workflow takes "Team Found? False" path
**Solutions:**
1. Check message field has valid team abbrev (2-4 uppercase letters)
2. Test API directly: `curl -H "Authorization: Bearer TOKEN" "URL/teams?abbrev=SKB"`
3. Verify team exists in target environment (pddev vs prod)

### API Call Failing
**Symptoms:** "API Success?" takes False path
**Solutions:**
1. Check HTTP Request body format (no JSON.stringify, no = prefix)
2. Verify `pack_requests` is an array in "Map Products" output
3. Check API credentials are correct
4. Review full error in node execution data

### Discord Notification Not Sending
**Symptoms:** No Discord message received
**Solutions:**
1. Verify webhook URL is correct
2. Test webhook directly with cURL
3. Check body has `content` field (required even with embeds)
4. Verify Discord channel hasn't deleted the webhook

### Unknown Product Code
**Symptoms:** `unknown_products` array has items, takes manual review path
**Solutions:**
1. Check Ko-fi product ID matches `PRODUCT_MAP` keys
2. Get product ID from Ko-fi product URL: `/s/{ID}`
3. Add new product to PRODUCT_MAP in "Map Products" code

---

## Maintenance Tasks

### Adding New Products
1. Get Ko-fi product ID from product URL
2. Edit "Map Products" Code node
3. Add entry to `PRODUCT_MAP`:
```javascript
'new-product-id': {
  name: 'Product Name',
  pack_type_id: 1,  // 1=Standard, 3=Premium, 8=Team Choice
  packs_per_quantity: 1
}
```
4. Save and test

### Changing API Endpoints
1. Edit "Lookup Team API" HTTP Request URL
2. Edit "Grant Packs" HTTP Request URL
3. Update "Parse & Validate" API_TOKEN if needed
4. Test thoroughly before activating

### Updating Discord Webhooks
1. Create new webhook in Discord
2. Update all Discord HTTP Request node URLs:
   - Discord Success
   - Discord Error
   - Discord Manual Review
   - Discord Thank You
3. Test each notification type

### Rotating Credentials
**Ko-fi Token:**
1. Get new token from Ko-fi dashboard
2. Update in "Parse & Validate" Code node
3. Test with cURL

**Paper Dynasty API Key:**
1. Generate new key in PD admin
2. Update "Paper Dynasty API" credential in n8n
3. Test API calls

---

## Future Enhancements

### Priority 1 (Next Session)
- [ ] Test Discord user ID → gmid lookup
- [ ] Add Subscription handling
- [ ] Implement currency product logic
- [ ] Test with real Ko-fi purchases

### Priority 2 (Nice to Have)
- [ ] Separate Discord channels for different notification types
- [ ] Email notifications for manual reviews
- [ ] Transaction history logging (database or file)
- [ ] Analytics dashboard for pack distributions

### Priority 3 (Optimization)
- [ ] Retry logic for failed transactions
- [ ] Batch processing for multiple purchases
- [ ] Scheduled reconciliation job
- [ ] Admin UI for product mapping

---

## Lessons Learned

### 1. n8n Community vs Enterprise
Community edition is powerful but limited:
- No custom variables (use hardcoded values in Code nodes)
- Code nodes can't make HTTP requests (use HTTP Request nodes)
- Works great for our use case with workarounds

### 2. Ko-fi Webhook Structure
- Form-urlencoded with JSON in `data` field
- Product codes are internal Ko-fi IDs, not custom
- `shop_items` can have multiple items in single order
- Always validate `verification_token`

### 3. Paper Dynasty API Integration
- Straightforward REST API
- Expects JSON arrays, not stringified
- Good error messages
- Dev environment perfect for testing

### 4. Discord Webhook Best Practices
- Always include `content` field
- Embeds are great for rich notifications
- @here mentions work for urgent alerts
- Test webhooks fail silently if deleted

### 5. Workflow Development Process
- Start simple, add complexity gradually
- Test each node individually
- Use debug fields (like `debug_info`) during development
- Document as you go

---

## Resources

**Documentation Created:**
- `/productivity/n8n/workflows/kofi-paper-dynasty.md` - Original detailed guide
- `/productivity/n8n/workflows/QUICK-START.md` - 30-minute setup guide
- `/productivity/n8n/workflows/kofi-testing-guide.md` - Testing procedures
- `/productivity/n8n/workflows/kofi-product-mapping-template.json` - Product config
- `/productivity/n8n/workflows/kofi-implementation-notes.md` - This file

**External References:**
- Ko-fi Webhook Docs: https://help.ko-fi.com/hc/en-us/articles/360004162298
- n8n Documentation: https://docs.n8n.io/
- Paper Dynasty API: See `/home/cal/.claude/skills/paper-dynasty/SKILL.md`

**Test Files:**
- Ko-fi payload structure: `/home/cal/Desktop/kofi-integration.md`

---

## Change Log

### 2025-11-13 - Initial Implementation
- Created complete Ko-fi → Paper Dynasty workflow
- Working in pddev environment
- Shop orders fully functional
- Donation notifications implemented
- Team identification via message extraction tested
- Discord notifications for all paths
- Ready for production deployment

---

**Next Session Goals:**
1. Test Discord user ID lookup
2. Deploy to production
3. Make first real Ko-fi purchase
4. Monitor and iterate

**Status:** ✅ Ready for production deployment
