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
6767import " dotenv/config" ;
6868import express from " express" ;
@@ -99,8 +99,170 @@ router.post("/checkout", async (req, res) => {
9999
100100export 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
105267Example 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
117279Use one or both:
118280
0 commit comments