refactor: delete dead legacy payment code from ApiService
Verified zero remaining callers for each before deleting (grepped
src/app for every method name individually), not assumed from the
earlier commit's dead-code note.
Deleted from api.service.ts:
- createPayment() - legacy direct QR creation (POST {qrBaseUrl}/qr)
- createCartPayment() - legacy /cart payment creation, client-sent amount
- createPaymentIntent() - superseded by @marketplaces/payment's gateway
- checkCartPaymentStatus(), checkCartCardPaymentStatus(), checkPaymentStatus()
- legacy QR/card status polls, superseded by the same gateway
- resolvePaymentQrId/resolvePaymentQrUrl/resolvePaymentLink/
resolveBankPaymentUrl - QrCreateResponse field-normalization helpers,
no longer had a caller once the methods above were gone
- Types: QrCreateRequest, QrCreateResponse, CartPaymentRequest,
PaymentIntentRequest, QrDynamicStatusResponse
- Fields: qrBaseUrl, cartPaymentPartnerId - no longer read by anything
- Imports: HttpHeaders, environment - no longer used in this file
Did NOT touch createOrder() or createCheckoutSession() - both still have
live callers in cart.component.ts, confirmed before deciding what to keep.
cart.component.ts: corrected the createPaymentIntent() comment, which
referenced the deleted method/helper names, to name what actually got
deleted instead of what was merely "dead as of that commit."
docs/backend/FRONTEND-API-SURFACE-COMPLETE.md: moved the 4 now-deleted
QR/card endpoints out of the "still live" legacy table into a dated removal
note - the doc's own premise is "every endpoint this codebase currently
calls," so it was wrong to leave them listed as called once they weren't.
Legacy-undocumented count corrected 15->11, total 97->93.
Verified: production build succeeds (no dangling references), 247/247 unit
tests, arch:check clean.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -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}/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 | `/items/{itemID}/notify-me` | `api.service.ts` | ❌ Undocumented — back-in-stock subscription |
|
||||||
| POST | `/purchase-email` | `api.service.ts` | ❌ Undocumented — post-purchase email collection |
|
| 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 |
|
| 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 |
|
| ✅ Specified | 54 |
|
||||||
| ⚠️ Inferred (needs confirmation) | 24 |
|
| ⚠️ Inferred (needs confirmation) | 24 |
|
||||||
| ❌ Undocumented (legacy) | 15 |
|
| ❌ Undocumented (legacy) | 11 |
|
||||||
| **Total distinct endpoints called** | **97** |
|
| **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.)
|
(§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.)
|
||||||
|
|
||||||
|
|||||||
@@ -333,12 +333,12 @@ export class CartComponent implements OnDestroy {
|
|||||||
/**
|
/**
|
||||||
* Superseded api.service.ts's createPaymentIntent (POST
|
* Superseded api.service.ts's createPaymentIntent (POST
|
||||||
* /api/v2/storefront/payments/intents, our own inferred contract) with
|
* /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/
|
* QrCreateResponse-based resolvePaymentQrId/resolvePaymentQrUrl/
|
||||||
* resolvePaymentLink/resolveBankPaymentUrl helpers on ApiService are dead
|
* resolvePaymentLink/resolveBankPaymentUrl helpers - all now deleted from
|
||||||
* code as of this change - left in place rather than deleted in the same
|
* ApiService, confirmed dead first (zero remaining callers) before removal.
|
||||||
* pass that adds a new external dependency, so a revert doesn't also need
|
|
||||||
* to resurrect deleted code.
|
|
||||||
*/
|
*/
|
||||||
private createPaymentIntent(
|
private createPaymentIntent(
|
||||||
session: import('../../services/api.service').CheckoutSessionResponse,
|
session: import('../../services/api.service').CheckoutSessionResponse,
|
||||||
|
|||||||
@@ -1,58 +1,11 @@
|
|||||||
import { Injectable } from '@angular/core';
|
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 { Observable, timer } from 'rxjs';
|
||||||
import { map, retry } from 'rxjs/operators';
|
import { map, retry } from 'rxjs/operators';
|
||||||
import { CategoryApiModel, DeliveryOption, Item, Subcategory } from '../models';
|
import { CategoryApiModel, DeliveryOption, Item, Subcategory } from '../models';
|
||||||
import { normalizeDeliveryOption, normalizeOptionalNumber } from '../utils/normalization.utils';
|
import { normalizeDeliveryOption, normalizeOptionalNumber } from '../utils/normalization.utils';
|
||||||
import { environment } from '../../environments/environment';
|
|
||||||
import { ApiConfigService } from '../core/config/api-config.service';
|
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.
|
* 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
|
* No `amount` or `price` field anywhere in this pair - the backend prices
|
||||||
@@ -88,18 +41,6 @@ export interface CheckoutSessionResponse {
|
|||||||
expiresAt: string;
|
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 {
|
export interface CreateOrderRequest {
|
||||||
/**
|
/**
|
||||||
* No `price` field: the backend must price each line item from its own
|
* No `price` field: the backend must price each line item from its own
|
||||||
@@ -120,28 +61,10 @@ export interface CreateOrderResponse {
|
|||||||
currency: string;
|
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({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class ApiService {
|
export class ApiService {
|
||||||
private readonly qrBaseUrl = (environment as any).qrApiUrl as string;
|
|
||||||
private readonly cartPaymentPartnerId = 'web-97ec-9c57-4dde-9037-3a68f7f83750';
|
|
||||||
|
|
||||||
private readonly retryConfig = {
|
private readonly retryConfig = {
|
||||||
count: 2,
|
count: 2,
|
||||||
delay: (_error: unknown, retryCount: number) => timer(Math.pow(2, retryCount) * 500)
|
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);
|
return this.http.post<{ message: string }>(`${this.baseUrl}/items/${itemID}/questiion`, body);
|
||||||
}
|
}
|
||||||
|
|
||||||
createPayment(payload: QrCreateRequest, headers?: { authorizationKey?: string; userIdValue?: string }): Observable<QrCreateResponse> {
|
|
||||||
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<QrCreateResponse>(`${this.qrBaseUrl}/qr`, payload, { headers: httpHeaders });
|
|
||||||
}
|
|
||||||
|
|
||||||
createCartPayment(payload: CartPaymentRequest): Observable<QrCreateResponse> {
|
|
||||||
return this.http.post<QrCreateResponse>(`${this.baseUrl}/cart`, payload);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a server-priced checkout session. Contract §5.2 - the frontend
|
* Creates a server-priced checkout session. Contract §5.2 - the frontend
|
||||||
* sends offer ids and quantities only; the response carries the total that
|
* sends offer ids and quantities only; the response carries the total that
|
||||||
@@ -694,16 +602,6 @@ export class ApiService {
|
|||||||
return this.http.post<CheckoutSessionResponse>('/api/v2/storefront/checkout', payload);
|
return this.http.post<CheckoutSessionResponse>('/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<QrCreateResponse> {
|
|
||||||
return this.http.post<QrCreateResponse>('/api/v2/storefront/payments/intents', payload);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Records the just-paid cart as a backoffice order (POST /orders). Fire-and-forget
|
* 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
|
* from the caller's perspective - a failure here must never block the existing
|
||||||
@@ -713,45 +611,6 @@ export class ApiService {
|
|||||||
return this.http.post<CreateOrderResponse>(`${this.baseUrl}/orders`, payload);
|
return this.http.post<CreateOrderResponse>(`${this.baseUrl}/orders`, payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
checkCartPaymentStatus(qrId: string): Observable<QrDynamicStatusResponse> {
|
|
||||||
return this.http.get<QrDynamicStatusResponse>(
|
|
||||||
`${this.qrBaseUrl}/qr/dynamic/${this.cartPaymentPartnerId}/${encodeURIComponent(qrId)}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
checkCartCardPaymentStatus(orderId: string): Observable<QrDynamicStatusResponse> {
|
|
||||||
return this.http.get<QrDynamicStatusResponse>(
|
|
||||||
`${this.qrBaseUrl}/card/${this.cartPaymentPartnerId}/${encodeURIComponent(orderId)}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
checkPaymentStatus(partnerQrId: string, qrId: string): Observable<QrDynamicStatusResponse> {
|
|
||||||
return this.http.get<QrDynamicStatusResponse>(
|
|
||||||
`${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: {
|
submitPurchaseEmail(emailData: {
|
||||||
email: string;
|
email: string;
|
||||||
phone?: string;
|
phone?: string;
|
||||||
|
|||||||
Reference in New Issue
Block a user