Monorepo for Aesthetic.Computer
aesthetic.computer
1# PayPal Integration Plan
2
3## Overview
4Integrate PayPal as a payment option on the give page (give.aesthetic.computer) alongside existing USD (Stripe), DKK, and Crypto options.
5
6## Account Details
7- **PayPal Account**: mail@aesthetic.computer
8- **API Environment**: Production (api-m.paypal.com)
9
10## Credentials Storage
11| Location | Purpose |
12|----------|---------|
13| `aesthetic-computer-vault/.env` | Local development, secure storage |
14| MongoDB secrets collection | Netlify functions runtime access |
15
16### Environment Variables
17```
18PAYPAL_CLIENT_ID=<stored in vault>
19PAYPAL_CLIENT_SECRET=<stored in vault>
20PAYPAL_API_URL=https://api-m.paypal.com
21```
22
23## API Architecture
24
25### PayPal Orders API v2
26- **Auth**: OAuth 2.0 (Client ID + Secret → Bearer Token)
27- **Create Order**: `POST /v2/checkout/orders`
28- **Capture Payment**: `POST /v2/checkout/orders/{id}/capture`
29
30### Flow
31```
321. User selects amount on give page
332. Frontend calls /api/paypal (Netlify function)
343. paypal.mjs creates order via PayPal API
354. Returns approval URL
365. User redirected to PayPal to approve
376. PayPal redirects back to give.aesthetic.computer?paypal_success=1
387. Frontend calls /api/paypal/capture with order ID
398. paypal.mjs captures payment
40```
41
42## Implementation Checklist
43
44### ✅ Completed
45- [x] PayPal tab in currency selector
46- [x] PayPal section UI (header, button, email)
47- [x] PayPal.me direct link button
48- [x] Copyable email address
49- [x] Multi-language support (EN, DA, DE, ES, ZH)
50- [x] PayPal QR code integration
51- [x] CSS styling (blue theme #0070ba)
52- [x] Give button "PP" cycling
53- [x] Credentials stored in vault
54
55### 🔄 In Progress
56- [ ] paypal.mjs Netlify endpoint
57- [ ] MongoDB secrets sync
58
59### ❌ Not Started
60- [ ] Custom amount input (like Stripe)
61- [ ] Payment capture flow
62- [ ] Success/failure handling on redirect
63- [ ] Transaction logging
64
65## Netlify Function: paypal.mjs
66
67### Endpoints
68```javascript
69// Create order
70POST /api/paypal
71Body: { amount: "10.00", currency: "USD" }
72Returns: { id: "ORDER_ID", approval_url: "https://paypal.com/..." }
73
74// Capture payment (optional, if using redirect flow)
75POST /api/paypal/capture
76Body: { order_id: "ORDER_ID" }
77Returns: { status: "COMPLETED", ... }
78```
79
80### Implementation Pattern
81```javascript
82// Follow give.js pattern
83import { MongoClient } from "mongodb";
84
85const getSecrets = async () => {
86 const client = await MongoClient.connect(process.env.MONGODB_URI);
87 const db = client.db("aesthetic");
88 return db.collection("secrets").findOne({ name: "paypal" });
89};
90
91const getAccessToken = async (clientId, clientSecret) => {
92 const response = await fetch("https://api-m.paypal.com/v1/oauth2/token", {
93 method: "POST",
94 headers: {
95 Authorization: `Basic ${Buffer.from(`${clientId}:${clientSecret}`).toString("base64")}`,
96 "Content-Type": "application/x-www-form-urlencoded",
97 },
98 body: "grant_type=client_credentials",
99 });
100 const data = await response.json();
101 return data.access_token;
102};
103
104const createOrder = async (accessToken, amount, currency = "USD") => {
105 const response = await fetch("https://api-m.paypal.com/v2/checkout/orders", {
106 method: "POST",
107 headers: {
108 Authorization: `Bearer ${accessToken}`,
109 "Content-Type": "application/json",
110 },
111 body: JSON.stringify({
112 intent: "CAPTURE",
113 purchase_units: [{
114 amount: {
115 currency_code: currency,
116 value: amount,
117 },
118 description: "Aesthetic Computer Contribution",
119 }],
120 application_context: {
121 return_url: "https://give.aesthetic.computer?paypal_success=1",
122 cancel_url: "https://give.aesthetic.computer?paypal_cancel=1",
123 brand_name: "Aesthetic Computer",
124 user_action: "PAY_NOW",
125 },
126 }),
127 });
128 return response.json();
129};
130```
131
132## Limitations
133- **No Monthly Subscriptions**: Orders API is one-time only
134 - Would need PayPal Subscriptions API for recurring
135 - User acknowledged this is acceptable
136- **Currency Support**: USD, EUR, GBP, etc. (no DKK via PayPal API)
137
138## QR Code
139- **Asset URL**: `https://assets.aesthetic.computer/images/paypal-qrcode.png`
140- **Links to**: PayPal.me/aestheticcomputer
141- **Purpose**: Quick mobile scanning for donations
142
143## Security Notes
1441. Never commit credentials to main repo
1452. Use vault for local dev
1463. Use MongoDB secrets for production
1474. Rotate credentials periodically
1485. Monitor for unauthorized transactions
149
150## Testing
1511. Use PayPal Sandbox for development testing
1522. Create sandbox accounts at developer.paypal.com
1533. Test with small amounts ($0.01) in production
154
155## Related Files
156- `system/public/give.aesthetic.computer/index.html` - Give page UI
157- `system/netlify/functions/give.js` - Stripe endpoint (reference)
158- `system/netlify/functions/paypal.mjs` - PayPal endpoint (to create)
159- `aesthetic-computer-vault/.env` - Credentials storage