Payment Link
๐ Overviewโ
internal/api/v1/billing/services/payment-link.service.js creates Stripe-hosted invoices for CRM contacts and returns short URLs that can be shared with customers. It handles contact lookup, on-demand Stripe customer creation, note linking, and line-item validation, all while respecting connected-account tokens.
Primary Controller: payment-link.controller.js
๐๏ธ Collections & Modelsโ
shared/models/contact.jsโ Locates CRM contacts and persists Stripe linkage metadata.
๐ External Dependenciesโ
- Stripe โ Customers API, Invoices API, Invoice Items API.
- DashClicks Router (
API_BASE_URL) โ Proxies customer creation via internal billing endpoint. - Short URL Utility โ
shared/utilities/short-urlgenerates branded short links. - Customer Service โ Links contacts to newly created Stripe customers.
- Logger Utility โ Captures failures when generating short URLs.
๐ Data Flowโ
flowchart TD
UI[Billing UI] -->|POST /payment-links| Controller
Controller --> Service
Service --> Contact[(CRM Contact)]
Service --> stripe[Stripe API]
Service --> Shortener
stripe --> HostedInvoice[Hosted Invoice URL]
Shortener --> HostedInvoice
๐งฉ Functionsโ
_findOrCreateCustomer({ contact, stripe_billing, stripe_config, account_id, req })โ
Private helper that ensures a Stripe customer exists and is linked to the contact.
- Builds a Stripe search query combining contact email and phone.
- Executes
stripe.customers.searchwith connected account context. - When no match is found:
- Calls internal
/v1/billing/customerendpoint to create a Stripe customer via Router. - Invokes
customerService.linkContactto sync DashClicks CRM with newly created Stripe metadata.
- Calls internal
- Returns Stripe customer object for downstream invoicing.
Edge Casesโ
- Search query gracefully handles missing email or phone.
- Address fields are mapped only when provided on the contact.
create({ req, contact_id, account_id, stripe_billing, stripe_config, currency, line_items, due_date })โ
Main entry point invoked by the controller.
- Loads contact by ID; throws
notFoundwhen missing. - Resolves Stripe customer via
_findOrCreateCustomer. - Creates a draft invoice with
collection_method = 'send_invoice',auto_advance = false, and human-readable description. - Iterates line items to create invoice items (supporting custom amount, quantity, or saved
price_id). - Finalises the invoice without sending to allow UI to orchestrate delivery.
- Attempts to shorten the hosted invoice URL; falls back to original on failure.
- Returns enriched invoice payload (with
short_urlwhen available).
Guards & Error Handlingโ
- Cleans up on partial failure: deletes draft invoice if any line item creation fails.
- Converts Stripe
due_dateparameter errors into actionablebadRequestresponses. - Logs but does not block when short URL creation fails.
๐งช Testing Notesโ
- Add fixture line items covering both
price_idand custom amount scenarios when extending functionality. - Error handling can be unit tested by mocking Stripe errors to confirm invoice deletion fallback.
โ ๏ธ Operational Considerationsโ
- Ensure the request includes OAuth headers when relaying to Router; service forwards
req.headers. - Short URL creation depends on downstream infrastructure; failures degrade gracefully to long URLs.