diff --git a/docs/backend/FRONTEND-API-SURFACE-COMPLETE.md b/docs/backend/FRONTEND-API-SURFACE-COMPLETE.md index 479e372..fe4eb52 100644 --- a/docs/backend/FRONTEND-API-SURFACE-COMPLETE.md +++ b/docs/backend/FRONTEND-API-SURFACE-COMPLETE.md @@ -68,13 +68,11 @@ These come from `BACKEND-API-REFERENCE.md`, not `docs/backend/`. Base URL is `en | POST | `/items/{itemID}/questiion` | `api.service.ts` | ❌ Undocumented — product Q&A (note: `questiion` typo is load-bearing, do not silently "fix" without checking the live backend uses the same typo) | | POST | `/items/{itemID}/notify-me` | `api.service.ts` | ❌ Undocumented — back-in-stock subscription | | POST | `/purchase-email` | `api.service.ts` | ❌ Undocumented — post-purchase email collection | -| POST | `{qrBaseUrl}/qr` | `api.service.ts` | ❌ Undocumented — direct QR payment creation | -| POST | `/cart` | `api.service.ts` | ❌ Undocumented — legacy payment creation, client-sent `amount` (superseded by §2 below for new checkout flow; **still live** for any caller not yet migrated) | -| GET | `{qrBaseUrl}/qr/dynamic/{partnerId}/{qrId}` | `api.service.ts` | ❌ Undocumented — QR payment status poll | -| GET | `{qrBaseUrl}/card/{partnerId}/{orderId}` | `api.service.ts` | ❌ Undocumented — card payment status poll | | POST | `/orders` | `api.service.ts` | ❌ Undocumented — records a paid cart as a backoffice order, fire-and-forget | -**Recommendation:** these 15 need their own contract doc if they are staying, or a deprecation timeline if `/api/v2/storefront/*` replaces them. Right now they are simply undocumented and live — the single biggest gap in `docs/backend/`. +**Recommendation:** these 11 need their own contract doc if they are staying, or a deprecation timeline if `/api/v2/storefront/*` replaces them. Right now they are simply undocumented and live — the single biggest gap in `docs/backend/`. + +**Removed 2026-08-21, no longer called by this codebase:** `POST {qrBaseUrl}/qr` (direct QR creation), `POST /cart` (legacy payment creation with client-sent `amount`), `GET {qrBaseUrl}/qr/dynamic/{partnerId}/{qrId}` and `GET {qrBaseUrl}/card/{partnerId}/{orderId}` (status polls). §2's checkout flow made these dead in code (`createPaymentIntent`) as of the earlier `@marketplaces/payment` integration commit; confirmed zero remaining callers and deleted the dead `ApiService` methods, request/response types, and unused fields (`qrBaseUrl`, `cartPaymentPartnerId`) in the same pass. If a real backend still serves these endpoints, they are now backend-only surface with no frontend caller — decide their fate independently of this doc. --- @@ -373,8 +371,8 @@ The frontend added a client-side guard against double-submitting checkout (a rea |---|---| | ✅ Specified | 54 | | ⚠️ Inferred (needs confirmation) | 24 | -| ❌ Undocumented (legacy) | 15 | -| **Total distinct endpoints called** | **97** | +| ❌ Undocumented (legacy) | 11 | +| **Total distinct endpoints called** | **93** | (§0's 3 Telegram endpoints count as Specified+live; its 4 ed25519 endpoints count as Specified+not-built — both are real, named endpoints with a known shape, distinct from §1's "no contract exists anywhere" undocumented status.) diff --git a/src/app/pages/cart/cart.component.ts b/src/app/pages/cart/cart.component.ts index f2a8523..6340b47 100644 --- a/src/app/pages/cart/cart.component.ts +++ b/src/app/pages/cart/cart.component.ts @@ -333,12 +333,12 @@ export class CartComponent implements OnDestroy { /** * Superseded api.service.ts's createPaymentIntent (POST * /api/v2/storefront/payments/intents, our own inferred contract) with - * @marketplaces/payment's real, published one. That method and the + * @marketplaces/payment's real, published one. That method, createPayment + * (legacy /qr), createCartPayment (legacy /cart), checkCartPaymentStatus, + * checkCartCardPaymentStatus, checkPaymentStatus, and the * QrCreateResponse-based resolvePaymentQrId/resolvePaymentQrUrl/ - * resolvePaymentLink/resolveBankPaymentUrl helpers on ApiService are dead - * code as of this change - left in place rather than deleted in the same - * pass that adds a new external dependency, so a revert doesn't also need - * to resurrect deleted code. + * resolvePaymentLink/resolveBankPaymentUrl helpers - all now deleted from + * ApiService, confirmed dead first (zero remaining callers) before removal. */ private createPaymentIntent( session: import('../../services/api.service').CheckoutSessionResponse, diff --git a/src/app/services/api.service.ts b/src/app/services/api.service.ts index 7553014..3ba62ec 100644 --- a/src/app/services/api.service.ts +++ b/src/app/services/api.service.ts @@ -1,58 +1,11 @@ import { Injectable } from '@angular/core'; -import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'; +import { HttpClient, HttpParams } from '@angular/common/http'; import { Observable, timer } from 'rxjs'; import { map, retry } from 'rxjs/operators'; import { CategoryApiModel, DeliveryOption, Item, Subcategory } from '../models'; import { normalizeDeliveryOption, normalizeOptionalNumber } from '../utils/normalization.utils'; -import { environment } from '../../environments/environment'; import { ApiConfigService } from '../core/config/api-config.service'; -export interface QrCreateRequest { - qrtype: 'QRDynamic'; - amount: number; - currency: 'RUB'; - partnerqrID?: string; - qrDescription?: string; - Userid?: string; - Reference?: string; - RedirectUrl?: string; -} - -export interface QrCreateResponse { - qrId?: string; - qrID?: string; - nspkID?: string; - nspkId?: string; - nspkurl?: string; - orderID?: string; - url?: string; - bankUrl?: string; - status?: string; - qrStatus?: string; - qrExpirationDate?: string; - qrTTL?: number; - payload?: string; - Payload?: string; - qrUrl?: string; - partnerqrID?: string | number; - partnerID?: string | number; - partnerId?: string | number; - PartnerID?: string | number; -} - -export interface CartPaymentRequest { - amount: number; - currency: string; - siteuserID: string; - siteorderID: string; - redirectUrl: string; - telegramUsername: string; - paymentMethod: 'qr' | 'card'; - qrDescription?: string; - customerID?: string; - items: Array<{ itemID: number; price: number; name: string; quantity?: number; delivery?: DeliveryOption[] }>; -} - /** * Server-authoritative checkout. Contract: PHASE-1-MONEY-FX-PAYMENTS-CONTRACT.md §5.2. * No `amount` or `price` field anywhere in this pair - the backend prices @@ -88,18 +41,6 @@ export interface CheckoutSessionResponse { expiresAt: string; } -/** - * References checkoutSessionId only - the amount charged is read - * server-side from the session, never re-sent by the client (contract §5.2). - * merchantReference is PARTNER-PROVISIONING-API-CONTRACT.md's RoutingContext - * field: our own correlation id, echoed back on every related event. - */ -export interface PaymentIntentRequest { - checkoutSessionId: string; - paymentMethod: 'qr' | 'card'; - merchantReference: string; -} - export interface CreateOrderRequest { /** * No `price` field: the backend must price each line item from its own @@ -120,28 +61,10 @@ export interface CreateOrderResponse { currency: string; } -export interface QrDynamicStatusResponse { - additionalInfo: string; - paymentPurpose: string; - amount: number; - code: string; - createDate: string; - currency: string; - order: string; - status: string; - qrId: string; - transactionDate: string; - transactionId: number; - qrExpirationDate: string; -} - @Injectable({ providedIn: 'root' }) export class ApiService { - private readonly qrBaseUrl = (environment as any).qrApiUrl as string; - private readonly cartPaymentPartnerId = 'web-97ec-9c57-4dde-9037-3a68f7f83750'; - private readonly retryConfig = { count: 2, delay: (_error: unknown, retryCount: number) => timer(Math.pow(2, retryCount) * 500) @@ -669,21 +592,6 @@ export class ApiService { return this.http.post<{ message: string }>(`${this.baseUrl}/items/${itemID}/questiion`, body); } - createPayment(payload: QrCreateRequest, headers?: { authorizationKey?: string; userIdValue?: string }): Observable { - let httpHeaders = new HttpHeaders(); - if (headers?.authorizationKey) { - httpHeaders = httpHeaders.set('authorization-key', headers.authorizationKey); - } - if (headers?.userIdValue) { - httpHeaders = httpHeaders.set('userid-value', headers.userIdValue); - } - return this.http.post(`${this.qrBaseUrl}/qr`, payload, { headers: httpHeaders }); - } - - createCartPayment(payload: CartPaymentRequest): Observable { - return this.http.post(`${this.baseUrl}/cart`, payload); - } - /** * Creates a server-priced checkout session. Contract §5.2 - the frontend * sends offer ids and quantities only; the response carries the total that @@ -694,16 +602,6 @@ export class ApiService { return this.http.post('/api/v2/storefront/checkout', payload); } - /** - * Creates a payment intent against an existing checkout session. Same - * response shape as createCartPayment (QrCreateResponse) - this replaces - * how the amount is determined, not the QR/card provider integration - * itself, which Phase 1 does not redesign. - */ - createPaymentIntent(payload: PaymentIntentRequest): Observable { - return this.http.post('/api/v2/storefront/payments/intents', payload); - } - /** * Records the just-paid cart as a backoffice order (POST /orders). Fire-and-forget * from the caller's perspective - a failure here must never block the existing @@ -713,45 +611,6 @@ export class ApiService { return this.http.post(`${this.baseUrl}/orders`, payload); } - checkCartPaymentStatus(qrId: string): Observable { - return this.http.get( - `${this.qrBaseUrl}/qr/dynamic/${this.cartPaymentPartnerId}/${encodeURIComponent(qrId)}` - ); - } - - checkCartCardPaymentStatus(orderId: string): Observable { - return this.http.get( - `${this.qrBaseUrl}/card/${this.cartPaymentPartnerId}/${encodeURIComponent(orderId)}` - ); - } - - checkPaymentStatus(partnerQrId: string, qrId: string): Observable { - return this.http.get( - `${this.qrBaseUrl}/qr/dynamic/${encodeURIComponent(partnerQrId)}/${encodeURIComponent(qrId)}` - ); - } - - resolvePaymentQrId(response: QrCreateResponse): string { - return response.qrId ?? response.qrID ?? response.nspkID ?? response.nspkId ?? response.orderID ?? ''; - } - - resolvePaymentLink(response: QrCreateResponse): string { - return response.nspkurl ?? response.Payload ?? response.payload ?? response.qrUrl ?? ''; - } - - resolveBankPaymentUrl(response: QrCreateResponse): string { - return response.bankUrl ?? response.url ?? ''; - } - - resolvePaymentQrUrl(response: QrCreateResponse): string { - const paymentLink = this.resolvePaymentLink(response); - if (paymentLink) { - return `https://api.qrserver.com/v1/create-qr-code/?size=256x256&margin=8&data=${encodeURIComponent(paymentLink)}`; - } - - return response.qrUrl ?? ''; - } - submitPurchaseEmail(emailData: { email: string; phone?: string;