Authenticated user retrieves their current default payment method (card brand, last4, expiry) and billing email from Stripe. Requires a valid JWT access token.
- Data is fetched live from Stripe
Customer.invoice_settings.default_payment_method— not cached in TalkIDE DB. - If no default payment method is registered, the endpoint returns
nullfor all card fields and the user’s account email asbillingEmail. - If
stripe_customer_idis null (user has never performed any billing action), the endpoint returns the “no payment method” placeholder without creating a Stripe Customer. - This endpoint is called on mount of
BillingSection.vueto populate the payment method row. - Related: UC-10001 — registration flow. UC-10003 — billing email update.
sequenceDiagram
actor User
User->>+FE: opens Profile → Billing & Usage section
FE->>FE: onMounted — trigger billing data load
FE->>+BE: GET /api/v1/users/me/billing/payment-method <br> Authorization: Bearer {accessToken}
BE->>BE: validate JWT access token
alt access token invalid or missing
BE-->>FE: 401 Unauthorized <br> ErrorResponse
end
BE->>DB: SELECT stripe_customer_id FROM users WHERE id = userId
alt stripe_customer_id is null
BE-->>FE: 200 OK <br> PaymentMethodResponse (all card fields null)
end
BE->>Stripe: stripe.customers.retrieve(customerId, { expand: ["invoice_settings.default_payment_method"] })
Stripe-->>BE: Customer { invoice_settings: { default_payment_method: PaymentMethod | null } }
alt no default payment method on Customer
BE-->>FE: 200 OK <br> PaymentMethodResponse (all card fields null)
end
BE->>-FE: 200 OK <br> PaymentMethodResponse
FE->>-User: display card brand + last4 + expiry + billing email
GET /api/v1/users/me/billing/payment-method (no request body; authentication via JWT Bearer token)
200 OK PaymentMethodResponse (card registered):
{
"brand": "visa",
"last4": "4242",
"expMonth": 12,
"expYear": 2028,
"billingEmail": "jane@example.com"
}
200 OK PaymentMethodResponse (no card registered):
{
"brand": null,
"last4": null,
"expMonth": null,
"expYear": null,
"billingEmail": "jane@example.com"
}
401 Unauthorized ErrorResponse:
{
"status": 401,
"code": "AUTHENTICATION_FAILED",
"message": "Access token is missing or invalid"
}
502 Bad Gateway (Stripe API unreachable) ErrorResponse:
{
"status": 502,
"code": "STRIPE_UNAVAILABLE",
"message": "Payment provider is temporarily unavailable. Please try again."
}
Frontend
Validations
| Field / Control | Constraints | Note |
|---|---|---|
| Payment method display | render placeholder “No payment method” when all card fields null | FE checks brand === null |
UX Guidelines
Payment method row states:
| State | Display |
|---|---|
| Loading | Skeleton row (shimmer) |
| No card registered | ”No payment method on file” text (fg-3) + “Add card” button (primary) |
| Card registered | Brand icon + ”•••• {last4}” + “Expires {expMonth}/{expYear}” + “Change” button (ghost) |
| Error (502) | Toast (rose, 5s): “Could not load payment method. Please try again.” |
Brand icon mapping (FE-side, CSS class or SVG asset):
brand value | Display label |
|---|---|
visa | Visa |
mastercard | Mastercard |
amex | American Express |
discover | Discover |
| (other) | capitalize brand value |
Backend
Validations
| Field | Constraints | Note |
|---|---|---|
JWT Authorization header | not_blank, valid signature, not expired | 401 if missing/invalid |
Test Cases
| GIVEN | WHEN | THEN |
|---|---|---|
| Authenticated user with no stripe_customer_id | GET /payment-method is called | 200 OK; all card fields null; billingEmail = user account email |
| Authenticated user with stripe_customer_id but no default payment method | GET /payment-method is called | 200 OK; all card fields null; billingEmail from Stripe Customer email |
| Authenticated user with Stripe test card 4242 set as default | GET /payment-method is called | 200 OK; brand = “visa”, last4 = “4242”, expMonth/expYear populated; billingEmail from Stripe Customer |
| No Authorization header | GET /payment-method is called | 401 AUTHENTICATION_FAILED returned |
| Stripe API unreachable | GET /payment-method is called | 502 STRIPE_UNAVAILABLE returned |
| User with stripe_customer_id that no longer exists in Stripe (edge case) | GET /payment-method is called | 502 STRIPE_UNAVAILABLE returned (or treat as no payment method — log warning, return null fields) |
Was this page helpful?
Thanks for the feedback.