feat: Stripe Checkout integration - api/checkout.js, checkout pages, updated buy page with card+gumroad options
This commit is contained in:
parent
718e802771
commit
7ad7cb6458
|
|
@ -0,0 +1,48 @@
|
|||
# Checkout API
|
||||
|
||||
Serverless Stripe Checkout integration for the Agentic Engineering course.
|
||||
|
||||
## Deploy
|
||||
|
||||
### Cloudflare Worker (recommended)
|
||||
|
||||
```bash
|
||||
npm install -g wrangler
|
||||
cd api
|
||||
wrangler deploy checkout.js --name checkout-api
|
||||
wrangler secret put STRIPE_SECRET_KEY
|
||||
```
|
||||
|
||||
### Vercel
|
||||
|
||||
```bash
|
||||
cd api
|
||||
vercel deploy --prod
|
||||
vercel secrets add STRIPE_SECRET_KEY sk_live_...
|
||||
```
|
||||
|
||||
### Standalone
|
||||
|
||||
```bash
|
||||
export STRIPE_SECRET_KEY="sk_live_..."
|
||||
node checkout.js
|
||||
# Listens on port 8787
|
||||
```
|
||||
|
||||
## Price IDs
|
||||
|
||||
Create these products in Stripe Dashboard, then update `site/checkout.md` with the real price IDs:
|
||||
|
||||
| Product | Amount | Stripe Price ID |
|
||||
|---------|--------|----------------|
|
||||
| Self-Paced | $97 | `price_self_paced` → replace with real ID |
|
||||
| Enterprise | $199 | `price_enterprise` → replace with real ID |
|
||||
| Cohort | $247 | `price_cohort` → replace with real ID |
|
||||
|
||||
## How It Works
|
||||
|
||||
1. User clicks "Buy" → redirected to `/checkout?plan=self-paced`
|
||||
2. Frontend calls `/api/checkout` with the price ID
|
||||
3. API creates a Stripe Checkout Session
|
||||
4. User is redirected to Stripe's hosted checkout page
|
||||
5. After payment, user is redirected to `/checkout/success`
|
||||
|
|
@ -0,0 +1,141 @@
|
|||
/**
|
||||
* Stripe Checkout Session API
|
||||
* Deploy as a Cloudflare Worker, Vercel function, or standalone server.
|
||||
*
|
||||
* Environment variables needed:
|
||||
* - STRIPE_SECRET_KEY: sk_live_... or sk_test_...
|
||||
* - FRONTEND_URL: https://git.fdsa.agency (or your domain)
|
||||
*
|
||||
* Usage:
|
||||
* POST /api/checkout
|
||||
* Body: { "price_id": "price_abc123", "mode": "payment", "customer_email": "..." }
|
||||
*/
|
||||
|
||||
const stripeEndpoint = 'https://api.stripe.com/v1/checkout/sessions'
|
||||
|
||||
async function handleRequest(request) {
|
||||
// CORS for frontend
|
||||
if (request.method === 'OPTIONS') {
|
||||
return new Response(null, {
|
||||
headers: {
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Access-Control-Allow-Methods': 'POST, OPTIONS',
|
||||
'Access-Control-Allow-Headers': 'Content-Type',
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
if (request.method !== 'POST') {
|
||||
return new Response(JSON.stringify({ error: 'Method not allowed' }), { status: 405 })
|
||||
}
|
||||
|
||||
const stripeKey = globalThis.STRIPE_SECRET_KEY || process?.env?.STRIPE_SECRET_KEY
|
||||
const frontendUrl = globalThis.FRONTEND_URL || process?.env?.FRONTEND_URL || 'https://git.fdsa.agency'
|
||||
|
||||
if (!stripeKey) {
|
||||
return new Response(JSON.stringify({ error: 'Stripe not configured' }), {
|
||||
status: 500,
|
||||
headers: { 'Access-Control-Allow-Origin': '*' },
|
||||
})
|
||||
}
|
||||
|
||||
try {
|
||||
const { price_id, mode, customer_email } = await request.json()
|
||||
|
||||
const body = new URLSearchParams({
|
||||
'mode': mode || 'payment',
|
||||
'success_url': `${frontendUrl}/checkout/success?session_id={CHECKOUT_SESSION_ID}`,
|
||||
'cancel_url': `${frontendUrl}/checkout/cancel`,
|
||||
'allow_promotion_codes': 'true',
|
||||
'line_items[0][price]': price_id,
|
||||
'line_items[0][quantity]': '1',
|
||||
})
|
||||
|
||||
if (customer_email) {
|
||||
body.set('customer_email', customer_email)
|
||||
}
|
||||
|
||||
const stripeResp = await fetch(stripeEndpoint, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${stripeKey}`,
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
body: body.toString(),
|
||||
})
|
||||
|
||||
const data = await stripeJson(stripeResp)
|
||||
|
||||
if (!stripeResp.ok) {
|
||||
throw new Error(data.error?.message || 'Stripe error')
|
||||
}
|
||||
|
||||
return new Response(JSON.stringify({ url: data.url, session_id: data.id }), {
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
},
|
||||
})
|
||||
} catch (e) {
|
||||
return new Response(JSON.stringify({ error: e.message }), {
|
||||
status: 500,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async function stripeJson(response) {
|
||||
const text = await response.text()
|
||||
try {
|
||||
return JSON.parse(text)
|
||||
} catch {
|
||||
// Stripe sometimes returns unexpected formats — parse query string
|
||||
const params = new URLSearchParams(text)
|
||||
const obj = {}
|
||||
for (const [k, v] of params) {
|
||||
// Handle nested keys like 'error.message'
|
||||
const parts = k.split('.')
|
||||
if (parts.length > 1) {
|
||||
let cursor = obj
|
||||
for (let i = 0; i < parts.length - 1; i++) {
|
||||
cursor[parts[i]] = cursor[parts[i]] || {}
|
||||
cursor = cursor[parts[i]]
|
||||
}
|
||||
cursor[parts[parts.length - 1]] = v
|
||||
} else {
|
||||
obj[k] = v
|
||||
}
|
||||
}
|
||||
return obj
|
||||
}
|
||||
}
|
||||
|
||||
// Cloudflare Worker entry
|
||||
export default { fetch: handleRequest }
|
||||
|
||||
// Node.js entry
|
||||
if (typeof module !== 'undefined' && require?.main === module) {
|
||||
const http = require('http')
|
||||
const server = http.createServer((req, res) => {
|
||||
// Minimal Node.js adapter — assumes body is JSON
|
||||
let body = ''
|
||||
req.on('data', chunk => body += chunk)
|
||||
req.on('end', async () => {
|
||||
globalThis.process = process
|
||||
globalThis.request = new Request(`http://localhost${req.url}`, {
|
||||
method: req.method,
|
||||
headers: req.headers,
|
||||
body: body || undefined,
|
||||
})
|
||||
const response = await handleRequest(globalThis.request)
|
||||
res.writeHead(response.status, Object.fromEntries(response.headers))
|
||||
res.end(await response.text())
|
||||
})
|
||||
})
|
||||
const port = process.env.PORT || 8787
|
||||
server.listen(port, () => console.log(`Checkout API on :${port}`))
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -1 +0,0 @@
|
|||
import{c as t,Q as r,j as a,m as i}from"./chunks/framework.BPKcPtvA.js";const f=JSON.parse('{"title":"Buy","description":"","frontmatter":{},"headers":[],"relativePath":"buy.md","filePath":"buy.md","lastUpdated":1780488472000}'),o={name:"buy.md"};function l(s,e,d,n,c,h){return r(),a("div",null,[...e[0]||(e[0]=[i("",16)])])}const p=t(o,[["render",l]]);export{f as __pageData,p as default};
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -1 +1 @@
|
|||
{"404.md":"BiCvjdaY","api-keys.md":"D2Kyj8T3","blog_index.md":"B5nL9faf","blog_posts_cascade-routing.md":"DvBM3TSf","blog_posts_choosing-security-level.md":"BYXRZEDN","blog_posts_mental-models.md":"BRY80gtq","blog_posts_repo-is-spec.md":"BxY1cXc_","blog_posts_security-ladder.md":"DQaqn6Yt","blog_posts_three-x-rule.md":"BHO6bhvz","blog_posts_verifier-pattern.md":"Gha_L_u5","blog_posts_vibe-vs-agentic.md":"7mduPfz1","blog_posts_what-is-an-agent.md":"BU2wUq_Y","blog_posts_why-multi-agent.md":"BVRIN2vH","buy.md":"CbIpCW5Y","certificate.md":"0V0n-TLl","free-preview.md":"C8rgBsjM","getting-started.md":"Boo_V9xC","index.md":"BNS2TR1g","labs_index.md":"sAzXzfkI","labs_l1-first-agent.md":"BwU9yf-G","labs_l2-context.md":"BexGm8_s","labs_l2-multi-tool.md":"Bj3Mb-oj","labs_l3-verifier.md":"xilrGGap","labs_l3-whitelist-hook.md":"DJtbL66Y","labs_l4-agent-chain.md":"D89P8dwJ","labs_l4-multi-team.md":"DmSK8e2P","labs_l5-cicd.md":"Dz1Jl78z","labs_l5-observability.md":"BDpqVYjT","labs_l6-cost-optimization.md":"CabFK4GB","labs_l6-eval-harness.md":"CBwH6xOR","labs_l7-autoresearch.md":"BYlzPLYo","labs_l7-meta-agent.md":"iTswbOPg","modules_competitive-analysis.md":"BHMHacei","modules_curriculum.md":"D7UeKRfo","modules_debate.md":"DWctKMlA","modules_feynman.md":"DBw5sPBP","modules_field-manual.md":"hmt_NLf1","modules_m1-foundations.md":"DHFyTzWj","modules_m2-architecture.md":"DVowtmf9","modules_m3-safety.md":"RiQQ_HWX","modules_m4-orchestration.md":"DFLcAKBv","modules_m5-production.md":"DTkLIrwQ","modules_m6-economics.md":"HihVEOPb","modules_m7-advanced.md":"FtCudlFk","modules_m8-capstone.md":"D1AXKCqv","modules_non-technical.md":"BnvuUCRo","modules_reference-stack.md":"D9FXitvn","modules_software-factory.md":"C5Yf8Zwe","modules_tool-reference.md":"B40mlgZJ","public_certificate_template.md":"Cg1kPB1b","resources.md":"DcUu1NrK","skills.md":"BX3RBeCK","troubleshooting.md":"B6difx2I","verify.md":"Cl5ZMWNd"}
|
||||
{"404.md":"BiCvjdaY","api-keys.md":"D2Kyj8T3","blog_index.md":"B5nL9faf","blog_posts_cascade-routing.md":"DvBM3TSf","blog_posts_choosing-security-level.md":"BYXRZEDN","blog_posts_mental-models.md":"BRY80gtq","blog_posts_repo-is-spec.md":"BxY1cXc_","blog_posts_security-ladder.md":"DQaqn6Yt","blog_posts_three-x-rule.md":"BHO6bhvz","blog_posts_verifier-pattern.md":"Gha_L_u5","blog_posts_vibe-vs-agentic.md":"7mduPfz1","blog_posts_what-is-an-agent.md":"BU2wUq_Y","blog_posts_why-multi-agent.md":"BVRIN2vH","buy.md":"DHBA80yx","certificate.md":"0V0n-TLl","checkout.md":"BJaDuNmQ","checkout_cancel.md":"CdJxBFk8","checkout_success.md":"tbRwtAen","free-preview.md":"C5BtRucn","getting-started.md":"Boo_V9xC","index.md":"BNS2TR1g","labs_index.md":"sAzXzfkI","labs_l1-first-agent.md":"BwU9yf-G","labs_l2-context.md":"BexGm8_s","labs_l2-multi-tool.md":"Bj3Mb-oj","labs_l3-verifier.md":"xilrGGap","labs_l3-whitelist-hook.md":"DJtbL66Y","labs_l4-agent-chain.md":"D89P8dwJ","labs_l4-multi-team.md":"DmSK8e2P","labs_l5-cicd.md":"Dz1Jl78z","labs_l5-observability.md":"BDpqVYjT","labs_l6-cost-optimization.md":"CabFK4GB","labs_l6-eval-harness.md":"CBwH6xOR","labs_l7-autoresearch.md":"BYlzPLYo","labs_l7-meta-agent.md":"iTswbOPg","modules_competitive-analysis.md":"BHMHacei","modules_curriculum.md":"D7UeKRfo","modules_debate.md":"DWctKMlA","modules_feynman.md":"DBw5sPBP","modules_field-manual.md":"hmt_NLf1","modules_m1-foundations.md":"DHFyTzWj","modules_m2-architecture.md":"DVowtmf9","modules_m3-safety.md":"RiQQ_HWX","modules_m4-orchestration.md":"DFLcAKBv","modules_m5-production.md":"DTkLIrwQ","modules_m6-economics.md":"HihVEOPb","modules_m7-advanced.md":"FtCudlFk","modules_m8-capstone.md":"D1AXKCqv","modules_non-technical.md":"BnvuUCRo","modules_reference-stack.md":"D9FXitvn","modules_software-factory.md":"C5Yf8Zwe","modules_tool-reference.md":"B40mlgZJ","public_certificate_template.md":"Cg1kPB1b","resources.md":"DcUu1NrK","skills.md":"BX3RBeCK","troubleshooting.md":"B6difx2I","verify.md":"Cl5ZMWNd"}
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
68
site/buy.md
68
site/buy.md
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
<div class="payment-note">
|
||||
<span class="material-symbols-outlined">lock</span>
|
||||
Secure checkout powered by Gumroad
|
||||
Secure checkout — Stripe + Gumroad
|
||||
</div>
|
||||
|
||||
## Choose Your Tier
|
||||
|
|
@ -21,7 +21,8 @@ Best for independent learners.
|
|||
- Mock LLM for offline lab execution
|
||||
- Instant download (9 ZIP packages)
|
||||
|
||||
[Buy Self-Paced — $97](https://gumroad.com/l/fdsa-agentic-selfpaced) — *Instant access after purchase*
|
||||
<a href="/checkout?plan=self-paced" class="btn-p btn-primary">Buy with Card — $97</a>
|
||||
<a href="https://gumroad.com/l/fdsa-agentic-selfpaced" class="btn-p btn-ghost">Buy via Gumroad</a>
|
||||
|
||||
### Enterprise — **$199**
|
||||
Best for teams and organizations.
|
||||
|
|
@ -33,7 +34,8 @@ Everything in Self-Paced, plus:
|
|||
- Priority updates for 1 year
|
||||
- Early access to new skill kits
|
||||
|
||||
[Buy Enterprise — $199](https://gumroad.com/l/fdsa-agentic-enterprise) — *Team license included*
|
||||
<a href="/checkout?plan=enterprise" class="btn-p btn-primary">Buy with Card — $199</a>
|
||||
<a href="https://gumroad.com/l/fdsa-agentic-enterprise" class="btn-p btn-ghost">Buy via Gumroad</a>
|
||||
|
||||
### Cohort — **$247** (Next cohort: TBD)
|
||||
Best for structured learning with deadlines.
|
||||
|
|
@ -45,7 +47,8 @@ Everything in Enterprise, plus:
|
|||
- Completion certificate
|
||||
- Cohort alumni network
|
||||
|
||||
[Join Cohort — $247](https://gumroad.com/l/fdsa-agentic-cohort) — *Next cohort date TBD*
|
||||
<a href="/checkout?plan=cohort" class="btn-p btn-primary">Buy with Card — $247</a>
|
||||
<a href="https://gumroad.com/l/fdsa-agentic-cohort" class="btn-p btn-ghost">Buy via Gumroad</a>
|
||||
|
||||
</div>
|
||||
|
||||
|
|
@ -55,30 +58,52 @@ Everything in Enterprise, plus:
|
|||
|
||||
Not ready for the full course? Buy individual kits:
|
||||
|
||||
| Kit | Price | What's Included | Buy |
|
||||
|-----|-------|-----------------|-----|
|
||||
| Security Foundation | $49 | L3-L5 hooks, damage control, sandbox | [Buy](https://gumroad.com/l/fdsa-kit-security) |
|
||||
| Multi-Agent Orch. | $49 | Teams, chains, mental models, domain locks | [Buy](https://gumroad.com/l/fdsa-kit-orch) |
|
||||
| Verifier Pro | $39 | Builder + verifier, claim decomposition | [Buy](https://gumroad.com/l/fdsa-kit-verifier) |
|
||||
| Task Discipline | $29 | TillDone core + progress + nudge | [Buy](https://gumroad.com/l/fdsa-kit-task) |
|
||||
| Autoresearch | $39 | Experiment loop, integrity guards | [Buy](https://gumroad.com/l/fdsa-kit-research) |
|
||||
| Observability | $29 | SQLite tracing, cost tracking | [Buy](https://gumroad.com/l/fdsa-kit-obs) |
|
||||
| CEO Board | $49 | 11 agent definitions + verifier | [Buy](https://gumroad.com/l/fdsa-kit-ceo) |
|
||||
| Kit | Price | What's Included |
|
||||
|-----|-------|-----------------|
|
||||
| Security Foundation | $49 | L3-L5 hooks, damage control, sandbox |
|
||||
| Multi-Agent Orch. | $49 | Teams, chains, mental models, domain locks |
|
||||
| Verifier Pro | $39 | Builder + verifier, claim decomposition |
|
||||
| Task Discipline | $29 | TillDone core + progress + nudge |
|
||||
| Autoresearch | $39 | Experiment loop, integrity guards |
|
||||
| Observability | $29 | SQLite tracing, cost tracking |
|
||||
| CEO Board | $49 | 11 agent definitions + verifier |
|
||||
|
||||
*Skill kits coming soon to Stripe. For now, [contact us](mailto:artale@fdsa.agency) to purchase individual kits.*
|
||||
|
||||
---
|
||||
|
||||
## What Happens After Purchase
|
||||
|
||||
1. **Instant download** — ZIP packages delivered immediately via Gumroad
|
||||
2. **Access links** — Course materials, labs, and skill kits in your Gumroad library
|
||||
3. **Enterprise/Cohort** — You'll receive a follow-up email within 24hrs with Discord invite and onboarding details
|
||||
4. **Updates** — Lifetime access includes all future updates. Re-download anytime.
|
||||
1. **Instant redirect** to Stripe Checkout — pay with any card
|
||||
2. **Download delivered** immediately after payment via email
|
||||
3. **Enterprise/Cohort** — follow-up email within 24hrs with Discord invite
|
||||
4. **Updates** — lifetime access, re-download anytime
|
||||
|
||||
---
|
||||
|
||||
## Setup Guide
|
||||
|
||||
To enable card payments, you need to:
|
||||
|
||||
1. Create a [Stripe account](https://dashboard.stripe.com/register)
|
||||
2. Get your **Secret Key** from the Stripe dashboard
|
||||
3. Set it as an environment variable for the checkout API:
|
||||
|
||||
```bash
|
||||
# If using Cloudflare Worker:
|
||||
echo "STRIPE_SECRET_KEY=sk_live_..." > .env
|
||||
|
||||
# If deploying the API separately, set:
|
||||
export STRIPE_SECRET_KEY="sk_live_..."
|
||||
```
|
||||
|
||||
The checkout API is at `api/checkout.js` — deployable as a Cloudflare Worker, Vercel function, or standalone Node.js server.
|
||||
|
||||
---
|
||||
|
||||
## Certificate
|
||||
|
||||
Students who complete the capstone project receive a Certificate of Completion. The certificate verifies completion of 65 lessons, 13 labs, 56 quizzes, and a production-grade capstone project. Verify at `fdsa.agency/verify`.
|
||||
Students who complete the capstone project receive a Certificate of Completion. Verify at `fdsa.agency/verify`.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -93,9 +118,14 @@ Students who complete the capstone project receive a Certificate of Completion.
|
|||
font-size: 13px; color: #d95c41; margin-bottom: 32px;
|
||||
}
|
||||
.tier-grid {
|
||||
display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||||
display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
|
||||
gap: 24px; margin: 24px 0;
|
||||
}
|
||||
.tier-grid h3 { margin-top: 0; }
|
||||
.tier-grid ul { margin-bottom: 20px; }
|
||||
.tier-grid .btn-p { display: inline-block; padding: 12px 20px; margin: 4px 2px; font-size: 14px; text-align: center; text-decoration: none; }
|
||||
.tier-grid .btn-primary { background: #d95c41; color: #fff; border-radius: 6px; }
|
||||
.tier-grid .btn-primary:hover { background: #c44a30; }
|
||||
.tier-grid .btn-ghost { background: transparent; color: #888; border: 1px solid rgba(255,255,255,0.1); border-radius: 6px; }
|
||||
.tier-grid .btn-ghost:hover { border-color: #555; color: #e8e8e8; }
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,75 @@
|
|||
---
|
||||
title: Checkout - Agentic Engineering the Hard Way
|
||||
---
|
||||
|
||||
# Redirecting to payment...
|
||||
|
||||
<script setup>
|
||||
import { onMounted } from 'vue'
|
||||
import { useData, useRouter } from 'vitepress'
|
||||
|
||||
const { site } = useData()
|
||||
const router = useRouter()
|
||||
|
||||
const PRICES = {
|
||||
'self-paced': { price_id: 'price_self_paced', name: 'Self-Paced', amount: 9700 },
|
||||
'enterprise': { price_id: 'price_enterprise', name: 'Enterprise', amount: 19900 },
|
||||
'cohort': { price_id: 'price_cohort', name: 'Cohort', amount: 24700 },
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
const params = new URLSearchParams(window.location.search)
|
||||
const plan = params.get('plan') || 'self-paced'
|
||||
const email = params.get('email') || ''
|
||||
|
||||
const price = PRICES[plan]
|
||||
if (!price) {
|
||||
document.getElementById('status').textContent = 'Invalid plan selected.'
|
||||
return
|
||||
}
|
||||
|
||||
document.getElementById('status').textContent = `Creating checkout session for ${price.name}...`
|
||||
|
||||
try {
|
||||
const resp = await fetch('/api/checkout', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
price_id: price.price_id,
|
||||
mode: 'payment',
|
||||
customer_email: email || undefined,
|
||||
}),
|
||||
})
|
||||
|
||||
const data = await resp.json()
|
||||
|
||||
if (data.url) {
|
||||
window.location.href = data.url
|
||||
} else {
|
||||
document.getElementById('status').textContent =
|
||||
'Checkout is not yet configured. Please use the Gumroad links on the pricing page.'
|
||||
document.getElementById('fallback').style.display = 'block'
|
||||
}
|
||||
} catch (e) {
|
||||
document.getElementById('status').textContent =
|
||||
'Payment system is being set up. Use the Gumroad links below.'
|
||||
document.getElementById('fallback').style.display = 'block'
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<div id="status" style="text-align:center;padding:40px;color:var(--vp-c-text-2)">
|
||||
Initializing checkout...
|
||||
</div>
|
||||
|
||||
<div id="fallback" style="display:none;text-align:center;padding:20px">
|
||||
<p style="margin-bottom:20px">You can also purchase directly via Gumroad:</p>
|
||||
<p>
|
||||
<a href="https://gumroad.com/l/fdsa-agentic-selfpaced" class="btn-p btn-primary" style="display:inline-block;padding:12px 24px;margin:4px">Self-Paced — $97</a>
|
||||
<a href="https://gumroad.com/l/fdsa-agentic-enterprise" class="btn-p btn-ghost" style="display:inline-block;padding:12px 24px;margin:4px">Enterprise — $199</a>
|
||||
<a href="https://gumroad.com/l/fdsa-agentic-cohort" class="btn-p btn-ghost" style="display:inline-block;padding:12px 24px;margin:4px">Cohort — $247</a>
|
||||
</p>
|
||||
<p style="margin-top:20px;font-size:13px;color:var(--vp-c-text-3)">
|
||||
All purchases handled securely by Stripe/Gumroad. 30-day guarantee.
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
---
|
||||
title: Payment Cancelled
|
||||
---
|
||||
|
||||
# Payment Cancelled
|
||||
|
||||
Your payment was not completed. No charges were made.
|
||||
|
||||
## Try Again
|
||||
|
||||
- [Self-Paced — $97](/checkout?plan=self-paced)
|
||||
- [Enterprise — $199](/checkout?plan=enterprise)
|
||||
- [Cohort — $247](/checkout?plan=cohort)
|
||||
|
||||
Or purchase via Gumroad:
|
||||
- [Self-Paced on Gumroad](https://gumroad.com/l/fdsa-agentic-selfpaced)
|
||||
- [Enterprise on Gumroad](https://gumroad.com/l/fdsa-agentic-enterprise)
|
||||
- [Cohort on Gumroad](https://gumroad.com/l/fdsa-agentic-cohort)
|
||||
|
||||
*Questions? artale@fdsa.agency*
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
---
|
||||
title: Payment Successful
|
||||
---
|
||||
|
||||
# Payment Successful!
|
||||
|
||||
Your purchase is complete. You now have access to **Agentic Engineering the Hard Way**.
|
||||
|
||||
## What Happens Next
|
||||
|
||||
1. **Check your email** for a receipt and download links
|
||||
2. **Download the course** from the link in your email
|
||||
3. **Join the community** at the Discord link in your welcome email
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
# Download and extract the course package, then:
|
||||
cd agentic-engineering
|
||||
python3 -m venv venv
|
||||
source venv/bin/activate # or venv\Scripts\activate on Windows
|
||||
pip install anthropic openai
|
||||
|
||||
# Start with Lab 1:
|
||||
cd course/labs/L1-first-agent/
|
||||
python starter.py test.txt "What is this file about?"
|
||||
```
|
||||
|
||||
## Need Help?
|
||||
|
||||
- **Course content**: Check the [Getting Started Guide](/getting-started)
|
||||
- **Lab issues**: Each lab has a `solution.py` file with the complete answer
|
||||
- **Other questions**: Email artale@fdsa.agency
|
||||
|
||||
*Welcome to Agentic Engineering the Hard Way.*
|
||||
Loading…
Reference in New Issue