Skip to content

Commit 3479b5d

Browse files
committed
feat: some updates
1 parent 8e33ff5 commit 3479b5d

10 files changed

Lines changed: 407 additions & 497 deletions

File tree

backend/src/routes/merchants.js

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import rateLimit from "express-rate-limit";
33
import { randomBytes } from "crypto";
44
import { supabase } from "../lib/supabase.js";
55
import { requireApiKeyAuth, requireSessionAuth, hashPassword } from "../lib/auth.js";
6+
import { generateSessionToken } from "../lib/sep10-auth.js";
67
import { getMerchantApiUsage } from "../lib/api-usage.js";
78
import { z } from "zod";
89
import { validateRequest } from "../lib/validation.js";
@@ -169,8 +170,11 @@ function createMerchantsRouter({
169170
throw insertError;
170171
}
171172

173+
const token = generateSessionToken(merchant.id, merchant.email);
174+
172175
res.status(201).json({
173176
message: "Merchant registered successfully",
177+
token,
174178
merchant: {
175179
id: merchant.id,
176180
email: merchant.email,
@@ -226,13 +230,6 @@ function createMerchantsRouter({
226230
throw error;
227231
}
228232

229-
// Check if merchant already exists
230-
const { data: existing } = await supabase
231-
.from("merchants")
232-
.select("id")
233-
.eq("email", email)
234-
.is("deleted_at", null)
235-
.maybeSingle();
236233
res.json({ api_key: newApiKey });
237234
} catch (err) {
238235
next(err);

frontend/content/docs/api-guide.md

Lines changed: 0 additions & 137 deletions
This file was deleted.

frontend/content/docs/api-guide.mdx

Lines changed: 166 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,8 @@ Save:
6161

6262
### 2. Add checkout route in your backend
6363

64-
`server/routes/checkout.js`
65-
64+
<FrameworkTabs>
65+
<FrameworkTab label="Express (Node)">
6666
```js
6767
import "dotenv/config";
6868
import express from "express";
@@ -99,8 +99,170 @@ router.post("/checkout", async (req, res) => {
9999

100100
export default router;
101101
```
102+
</FrameworkTab>
103+
<FrameworkTab label="Next.js (App Router)">
104+
```typescript
105+
import { NextResponse } from 'next/server';
106+
107+
export async function POST(req: Request) {
108+
try {
109+
const body = await req.json();
110+
const API_URL = process.env.PLUTO_API_URL || "https://pluto-api.up.railway.app";
111+
112+
const payload = {
113+
amount: body.amount,
114+
asset: "USDC",
115+
asset_issuer: process.env.USDC_ISSUER,
116+
recipient: process.env.MERCHANT_STELLAR_RECIPIENT,
117+
metadata: { cart_id: body.cartId },
118+
};
119+
120+
const r = await fetch(`${API_URL}/api/create-payment`, {
121+
method: "POST",
122+
headers: {
123+
"Content-Type": "application/json",
124+
"x-api-key": process.env.PLUTO_API_KEY!,
125+
},
126+
body: JSON.stringify(payload),
127+
});
128+
129+
const data = await r.json();
130+
return NextResponse.json(data, { status: r.status });
131+
} catch (err: any) {
132+
return NextResponse.json({ error: err.message }, { status: 500 });
133+
}
134+
}
135+
```
136+
</FrameworkTab>
137+
<FrameworkTab label="Python (Django)">
138+
```python
139+
import os
140+
import requests
141+
import json
142+
from django.http import JsonResponse
143+
from django.views.decorators.csrf import csrf_exempt
144+
from django.views.decorators.http import require_POST
145+
146+
@csrf_exempt
147+
@require_POST
148+
def create_checkout(request):
149+
try:
150+
body = json.loads(request.body)
151+
152+
payload = {
153+
"amount": body.get("amount"),
154+
"asset": "USDC",
155+
# Optional: Standard issuer is used if USDC_ISSUER is missing
156+
"asset_issuer": os.getenv("USDC_ISSUER"),
157+
"recipient": os.getenv("MERCHANT_STELLAR_RECIPIENT"),
158+
"metadata": {"cart_id": body.get("cartId")}
159+
}
160+
161+
headers = {
162+
"Content-Type": "application/json",
163+
"x-api-key": os.getenv("PLUTO_API_KEY")
164+
}
165+
166+
url = f"{os.getenv('PLUTO_API_URL')}/api/create-payment"
167+
response = requests.post(url, json=payload, headers=headers)
168+
169+
return JsonResponse(response.json(), status=response.status_code)
170+
except Exception as e:
171+
return JsonResponse({"error": str(e)}, status=500)
172+
```
173+
</FrameworkTab>
174+
</FrameworkTabs>
175+
176+
### 3. Call your backend from the frontend
177+
178+
Once your backend route is ready, trigger it seamlessly from your frontend customer checkout page:
179+
180+
<FrameworkTabs>
181+
<FrameworkTab label="React">
182+
```tsx
183+
import { useState } from 'react';
184+
185+
export default function CheckoutButton({ amount, cartId }) {
186+
const [loading, setLoading] = useState(false);
187+
188+
const handleCheckout = async () => {
189+
setLoading(true);
190+
try {
191+
const response = await fetch('/api/checkout', {
192+
method: 'POST',
193+
headers: { 'Content-Type': 'application/json' },
194+
body: JSON.stringify({ amount, cartId })
195+
});
196+
197+
const session = await response.json();
198+
199+
if (session.payment_link) {
200+
// Redirect standard browser to PLUTO UI
201+
window.location.href = session.payment_link;
202+
}
203+
} catch (err) {
204+
console.error("Checkout error:", err);
205+
} finally {
206+
setLoading(false);
207+
}
208+
};
209+
210+
return (
211+
<button onClick={handleCheckout} disabled={loading}>
212+
{loading ? 'Processing...' : `Pay $${amount} with Crypto`}
213+
</button>
214+
);
215+
}
216+
```
217+
</FrameworkTab>
218+
<FrameworkTab label="Vue 3 (Composition API)">
219+
```vue
220+
<script setup>
221+
import { ref } from 'vue';
222+
223+
const props = defineProps({
224+
amount: Number,
225+
cartId: String
226+
});
227+
228+
const loading = ref(false);
229+
230+
const handleCheckout = async () => {
231+
loading.value = true;
232+
try {
233+
const response = await fetch('/api/checkout', {
234+
method: 'POST',
235+
headers: { 'Content-Type': 'application/json' },
236+
body: JSON.stringify({
237+
amount: props.amount,
238+
cartId: props.cartId
239+
})
240+
});
241+
242+
const session = await response.json();
243+
244+
if (session.payment_link) {
245+
// Redirect standard browser to PLUTO UI
246+
window.location.href = session.payment_link;
247+
}
248+
} catch (err) {
249+
console.error("Checkout error:", err);
250+
} finally {
251+
loading.value = false;
252+
}
253+
};
254+
</script>
255+
256+
<template>
257+
<button @click="handleCheckout" :disabled="loading">
258+
{{ loading ? 'Processing...' : `Pay $${amount} with Crypto` }}
259+
</button>
260+
</template>
261+
```
262+
</FrameworkTab>
263+
</FrameworkTabs>
102264

103-
### 3. Redirect customer to returned `payment_link`
265+
### 4. Redirect customer to returned `payment_link`
104266

105267
Example response:
106268

@@ -112,7 +274,7 @@ Example response:
112274
}
113275
```
114276

115-
### 4. Track result in your app
277+
### 5. Track result in your app
116278

117279
Use one or both:
118280

0 commit comments

Comments
 (0)