Skip to content

[Phase 0c] PayPal one-off: create-paypal-order + capture-paypal-order (~4h) #103

@TortoiseWolfe

Description

@TortoiseWolfe

Part of epic #100.

Functions to ship

1. create-paypal-order (~2h)

  • Browser caller: src/lib/payments/paypal.ts:74
  • Request body: { payment_intent_id: string }
  • Logic:
    1. CORS + auth
    2. Load payment_intents row by id, verify owner
    3. Get PayPal access token via OAuth (use PAYPAL_CLIENT_SECRET + browser PAYPAL_CLIENT_ID)
    4. POST /v2/checkout/orders with intent: 'CAPTURE' and the intent's amount/currency
    5. Store PayPal order.id on payment_intents row (paypal_order_id column)
    6. Return { orderId: order.id }
  • PayPal Orders v2 docs: https://developer.paypal.com/docs/api/orders/v2/

2. capture-paypal-order (~2h)

  • Browser caller: src/lib/payments/paypal.ts:101
  • Request body: { order_id: string }
  • Logic:
    1. CORS + auth
    2. POST /v2/checkout/orders/{order_id}/capture
    3. Map PayPal status → payment_results row insert (status='completed' or status='failed' with categorized error)
    4. Return { status, capture_id }

Tests

  • Deno unit tests mocking PayPal REST calls
  • Un-skip relevant tests/e2e/payment/02-paypal-subscription.spec.ts tests for the one-off flow

Acceptance

  • Both functions exist
  • Deno tests pass
  • Deploy succeeds
  • /payment-demo PayPal tab → sandbox buyer account → completes, payment_results row created
  • Provider response (PAYMENT.CAPTURE.COMPLETED webhook) ALSO lands a webhook_events row for idempotency cross-check

🤖 Created from audit on 2026-05-20

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestgap-auditIdentified during 2026-04-25 planned-vs-shipped auditpriority:p1High — fix soon (stability hotspot, low-hanging fruit, single-decision unlocks)

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions