Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/ALEJ4NDRO2025/urban-store/llms.txt

Use this file to discover all available pages before exploring further.

Urban Store processes payments through Stripe using the PaymentIntents API. All transactions are denominated in Colombian Peso (COP). The integration is intentionally minimal: there are no webhooks — the frontend drives the full payment lifecycle via two API calls bookending the Stripe-hosted card collection step. Stripe’s payment_intent_id is stored on the order document for audit and reconciliation purposes.

Payment Flow

1

Create PaymentIntent

The frontend calls POST /api/payments/create-payment-intent/ with the order_id. The backend validates that the order belongs to the authenticated user and is still in pending status, then creates a Stripe PaymentIntent for order.total × 100 centavos in cop currency. The payment_intent_id is saved on the order document and the client_secret is returned to the frontend.
POST /api/payments/create-payment-intent/
Authorization: Bearer <token>
Content-Type: application/json

{
  "order_id": "665f1a2b3c4d5e6f7a8b9c0d"
}
Response:
{
  "client_secret": "pi_3OxYZ1234567890_secret_abc123"
}
2

Frontend Collects Card Details

Using the client_secret, the frontend mounts Stripe’s <PaymentElement> component (from @stripe/react-stripe-js). The customer enters their card details directly in the Stripe-hosted UI — card data never touches Urban Store’s servers.
import { PaymentElement, useStripe, useElements } from '@stripe/react-stripe-js';

// client_secret passed to <Elements options={{ clientSecret }}> wrapper
<PaymentElement />
3

Stripe Processes the Payment

The frontend calls stripe.confirmPayment() (or stripe.confirmCardPayment()) with the client_secret. Stripe validates the card, charges it, and transitions the PaymentIntent to succeeded (or returns an error).
4

Confirm Payment with Backend

The frontend calls POST /api/payments/confirm-payment/ with both order_id and payment_intent_id. The backend retrieves the PaymentIntent from Stripe and checks its status. If succeeded, the order is updated to paid and paid_at is set. The expires_at field is cleared (None) so the order is no longer subject to TTL cancellation.
POST /api/payments/confirm-payment/
Authorization: Bearer <token>
Content-Type: application/json

{
  "order_id": "665f1a2b3c4d5e6f7a8b9c0d",
  "payment_intent_id": "pi_3OxYZ1234567890"
}
Response (success):
{
  "status": "paid",
  "message": "Pago confirmado"
}
Response (not yet completed):
{
  "status": "requires_payment_method",
  "message": "Pago no completado. Estado actual: requires_payment_method"
}

Currency and Amount Conversion

All PaymentIntents are created in cop (Colombian Peso). Stripe requires amounts in the smallest currency unit (centavos), so the order total is multiplied by 100:
intent = stripe.PaymentIntent.create(
    amount=int(order.total * 100),   # e.g. 149900 COP → 14990000 centavos
    currency='cop',
    metadata={'order_id': str(order.id)}
)
The Stripe metadata field carries the order_id, which is useful for manual reconciliation or future webhook integration. It is not used programmatically by the current backend.

Order Validation

Before creating a PaymentIntent, the backend enforces two conditions:
  1. Ownership — the order must exist and its user_id must match the email extracted from the JWT. Orders belonging to other users return 404.
  2. Status — the order must be in pending status. Any other status (including paid, cancelled, or shipped) returns 400 with "La orden ya fue procesada".

Frontend Libraries

The frontend uses the official Stripe React integration:
PackageVersion
@stripe/react-stripe-js^6.2.0
@stripe/stripe-js^9.2.0
The publishable key must be set in the frontend environment as NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY, and the Django backend requires STRIPE_SECRET_KEY in its settings.

Error Handling

Any stripe.error.StripeError raised during PaymentIntent creation or retrieval is caught and returned as a 400 Bad Request:
{
  "error": "No such payment_intent: 'pi_invalid_id'"
}
The frontend should display this message to the user and allow them to retry.

Test Card

During development, use Stripe’s standard test card to simulate a successful payment:
FieldValue
Card number4242 4242 4242 4242
ExpiryAny future date (e.g. 12/30)
CVCAny 3 digits (e.g. 123)
ZIPAny value
Always ensure STRIPE_SECRET_KEY is set to a test key (sk_test_...) in development and staging environments. Using a live key (sk_live_...) in development will result in real charges. Never commit either key to version control.

Build docs developers (and LLMs) love