.. _redirect_to_checkout: Redirect to Checkout ==================== After creating a payment prompt, redirect your customer to the MiraclePay hosted checkout page where they can complete the payment. Checkout URL Structure ---------------------- The ``checkoutUrl`` returned from the Create Payment Prompt API has this format: .. code-block:: text https://checkout.miraclepay.com/?sessionId= **Example:** .. code-block:: text https://checkout.miraclepay.com/?sessionId=550e8400-e29b-41d4-a716-446655440000 Redirect Methods ---------------- Server-Side Redirect (Recommended) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Redirect the user from your backend after creating the payment: **Node.js / Express:** .. code-block:: javascript app.post('/checkout', async (req, res) => { const payment = await createPayment(req.body.amount, 'eth'); // Store payment ID with order await Order.update(req.body.orderId, { paymentPromptId: payment.prompt.id }); // Redirect to checkout res.redirect(payment.checkoutUrl); }); Client-Side Redirect ~~~~~~~~~~~~~~~~~~~~ If your frontend calls your API, return the checkout URL for client-side redirect: **API Response:** .. code-block:: json { "success": true, "checkoutUrl": "https://checkout.miraclepay.com/?sessionId=550e8400..." } **Frontend JavaScript:** .. code-block:: javascript async function handlePayment() { const response = await fetch('/api/create-payment', { method: 'POST', body: JSON.stringify({ amount: '50.00', orderId: 'ORD-123' }), }); const { checkoutUrl } = await response.json(); // Redirect to MiraclePay checkout window.location.href = checkoutUrl; } Checkout Flow ------------- Once redirected, the customer experiences this flow: .. code-block:: text Your Site ──► MiraclePay Checkout ──► Select Wallet ──► Connect Wallet │ ▼ Confirm Payment │ ▼ Transaction Status / \ Success Failed │ │ ▼ ▼ Payment Complete Retry/Cancel **Checkout Page Features:** 1. **Wallet Selection**: Customer chooses their crypto wallet 2. **Amount Display**: Shows payment amount in USD and crypto equivalent 3. **QR Code**: For mobile wallet scanning 4. **Transaction Confirmation**: Real-time status updates 5. **Expiration Timer**: Shows remaining time to complete payment Post-Payment Handling --------------------- Automatic Redirect (Recommended) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If you provide a ``redirectUrl`` when creating the payment prompt, MiraclePay will automatically redirect the customer back to your site after payment: - **Successful payment**: Customer is auto-redirected after 5 seconds, with a "Return to store" button for immediate redirect - **Expired or cancelled**: A "Return to store" button is shown (no auto-redirect) The redirect URL will have query parameters appended: .. code-block:: text https://shop.example.com/order/123/complete?promptId=550e8400-...&status=successful **Query parameters:** .. list-table:: :widths: 20 80 :header-rows: 1 * - Parameter - Description * - ``promptId`` - The payment prompt UUID * - ``status`` - Payment result: ``successful``, ``expired``, or ``cancelled`` .. warning:: **Do not trust the redirect query parameters as proof of payment.** A customer could manually navigate to your redirect URL with a forged ``status=successful`` parameter. Always verify the payment server-side via ``GET /external/payments/prompt/:id``. Manual Polling (Without Redirect URL) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If you don't provide a ``redirectUrl``, you need to verify the payment status yourself. The checkout page will show "You can now safely close this window" after completion. **Recommended approach:** 1. **Before redirect**: Show a "Processing payment..." page 2. **Poll for status**: Check payment status every 5 seconds 3. **Update order**: Mark order as paid when status is ``successful`` 4. **Show confirmation**: Display order confirmation to customer **Example polling implementation:** .. code-block:: javascript async function pollPaymentStatus(promptId) { const maxAttempts = 60; // 5 minutes at 5-second intervals let attempts = 0; while (attempts < maxAttempts) { const status = await getPaymentStatus(promptId); switch (status.status) { case 'successful': return { success: true, status }; case 'unsuccessful': case 'expired': case 'cancelled': return { success: false, status }; case 'pending': // Continue polling break; } await new Promise(resolve => setTimeout(resolve, 5000)); attempts++; } throw new Error('Payment status polling timeout'); } Payment Expiration ------------------ Payment prompts expire **24 hours** after creation. If the customer doesn't complete payment: - The payment status changes to ``expired`` - The checkout URL becomes invalid - You should create a new payment prompt if the customer wants to retry .. tip:: Display the expiration time to customers and remind them to complete payment promptly.