Skip to content

fix: complete mobile payment workflow end-to-end#337

Merged
bllr777 merged 1 commit into
mainfrom
feature/auto-promote-job-lifecycle-status
May 25, 2026
Merged

fix: complete mobile payment workflow end-to-end#337
bllr777 merged 1 commit into
mainfrom
feature/auto-promote-job-lifecycle-status

Conversation

@bllr777
Copy link
Copy Markdown
Collaborator

@bllr777 bllr777 commented May 25, 2026

Summary

Closes the loop on mobile payments — Stripe now returns a hosted checkout URL, invoices are auto-created when missing, and the webhook correctly marks invoices paid after the customer pays.

Changes

Invoice auto-resolution (\EnsureInvoiceForPaymentAsync)

When a mobile worker taps Collect Payment, the API runs this waterfall:

  1. Existing invoice for the job → use \BalanceDue\
  2. Accepted estimate on the job → auto-create invoice from estimate line items (Option A)
  3. No estimate + amount provided → create quick draft invoice with single line item (Option B)

Stripe checkout URL fix

  • \StripePaymentProcessor: added \CreateOneTimeCheckoutSessionAsync\ using \Stripe.Checkout.Session\ (mode: payment) — returns a hosted redirect URL instead of a \clientSecret\
  • \IPaymentProcessor\ / \SquarePaymentProcessor: interface + Square implementation added (Square already used hosted checkout)
  • \PaymentController.Checkout: routes one-time payments through the new method; accepts \jobId\ to trigger invoice auto-resolution

Stripe webhook fix

\HandleCheckoutSessionAsync\ previously returned immediately for any session without a \SubscriptionId\ (i.e. every one-time payment). Now:

  • Reads \invoiceId\ from session metadata
  • Calls \MarkPaidAsync\
  • Fires client + org payment notifications
  • Marks onboarding \ReceivePayment\ step complete

Assignment BalanceDue surfacing

  • \AssignmentDto: added \BalanceDue\ and \InvoiceId\
  • \AssignmentService: pre-fetches latest invoice per job in \GetAssignmentsAsync\

Related

Pair PR in JobFlow-Mobile: fix: wire mobile payment flow end-to-end

- AssignmentDto: add BalanceDue and InvoiceId fields
- AssignmentService: pre-fetch latest invoice per job, populate BalanceDue on assignments
- IPaymentProcessor: add CreateOneTimeCheckoutSessionAsync
- StripePaymentProcessor: implement CreateOneTimeCheckoutSessionAsync using Stripe Checkout
  Sessions (hosted page with redirect URL) instead of PaymentIntents (client secret only)
- SquarePaymentProcessor: implement CreateOneTimeCheckoutSessionAsync (delegates to
  existing hosted checkout flow)
- PaymentSessionRequest: add JobId field
- PaymentController.Checkout: route one-time payments through CreateOneTimeCheckoutSessionAsync;
  when JobId provided without InvoiceId, call EnsureInvoiceForPaymentAsync to auto-resolve
- IInvoiceService / InvoiceService: add EnsureInvoiceForPaymentAsync - finds existing invoice,
  falls back to creating from accepted estimate (Option A), falls back to quick single-line-item
  draft invoice from provided amount (Option B)
- StripeWebhookService: fix HandleCheckoutSessionAsync to handle mode=payment sessions
  (previously bailed immediately on missing SubscriptionId); on success calls MarkPaidAsync,
  fires client and org payment notifications, marks onboarding step complete
@bllr777 bllr777 merged commit d2c8b57 into main May 25, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant