Objective
- Let clients complete credit card payments on the client portal by reliably selecting expiration month and year on pay-beta checkout.
- Remove false impressions that expiration is locked (greyed out) when the form should be editable for all portal visitors—clients and staff opening the same portal from a work order.
Background
- Field report: expiration appeared greyed out with confusing “zeros and ones”; client could not choose month/year. Example property: 3180 Halifax Dr (job for tomorrow). Client: Lisa Conklin (762-699-0402 per Slack report).
- Slack report / transcript
- Checkout path: Attik confirmed jobs with Guardian enabled →
/client/job/{slug}/pay-beta→ Credit Card tab (PaymentBetaClient→CreditCardTab). - Code review (no role-based lock): Month and Year are separate HeroUI
Selectfields inCreditCardTab.tsx. They are notisDisabledin code, not gated on card number length (no requirement for 12 or 16 digits before expiry is editable), and not different for clients vs internal users—staff open the same client portal URL (often withaccess_tokenfrom the work order) and the samepay-betapage. - Card number validation (13–19 digits, Luhn) runs on Pay submit only, not to enable expiry fields. Only the Pay button disables when payment amount or billing ZIP is invalid.
- Likely repro gap: Internal repro on desktop may succeed while the client was on phone (per support call). Plausible causes to validate: mobile Safari/Chrome + HeroUI
Select(popover open/close, blur-on-scroll), browser/password-manager autofill overlaying grey read-only expiration UI, or broken interaction withSelectrefs (typed asHTMLSelectElementbut not a native<select>). Expiry fields also lackautoComplete="cc-exp-month"/cc-exp-year"while card number and CVV have autocomplete hints. - Client quote (from report): “Yeah, you can't put it in there, it's just all zeros and one… my card expires in November I just went down eleven spaces… I have no way of guessing what the years are cause it's zeroes in one.”
Scope
Frontend
- Primary UI:
attik-frontend/src/app/client/job/[slug]/pay-beta/components/CreditCardTab.tsx— Month/YearSelect+SelectItem(~lines 584–642); cardInput; CVVInput; submit handler and validation in same file. - Host page:
attik-frontend/src/app/client/job/[slug]/pay-beta/PaymentBetaClient.tsx,page.tsx(Guardian gate,access_token/portalPayerIdfor logging only—not form disable logic). - Portal entry from job:
attik-frontend/src/app/client/job/[slug]/components/JobAccordion.tsx(pay-betalink); staff portal open:attik-frontend/src/app/tools/inspections/[id]/components/WorkorderActionsDropdown.tsx(same client URL in new tab). - Payment submit:
attik-frontend/src/app/client/job/[slug]/pay-beta/actions/processCardPayment.ts→ backendguardian-payments/process-card(expiryMMYY). - Existing tests:
tests/app/client/job/pay-beta/CreditCardTab.fee.test.tsx(fees only); no mobile/expiry interaction coverage today.
Backend
- Card processing and expiry format validation:
attik-backend/src/routes/guardianPayments.ts(expectsexpiryas 4-digitMMYYon submit). Unlikely root cause of greyed UI, but confirm end-to-end after frontend fix.
Investigation outcomes (draft acceptance)
- [ ] Reproduce on mobile Safari and desktop Chrome using same job slug /
pay-betaURL. - [ ] Confirm whether failure is HeroUI
Select, autofill overlay, or other—and document client device/browser when possible. - [ ] Fix so Month/Year are reliably selectable on supported client devices; add regression test where feasible.
- [ ] Optional hardening:
textValueonSelectItem, expiryautoComplete, consistent 2-digit year handling invalidateExpiryvs year option keys.
Out of scope (unless investigation proves otherwise)
- Spectora-native payment path (Spectora jobs are blocked from Guardian pay-beta).
- Requiring a specific card length before enabling expiry (not current behavior; do not add unless product asks).
References
- Slack report / client transcript
attik-frontend/src/app/client/job/[slug]/pay-beta/components/CreditCardTab.tsxattik-frontend/src/app/client/job/[slug]/pay-beta/PaymentBetaClient.tsxattik-frontend/src/app/client/job/[slug]/pay-beta/page.tsxattik-frontend/src/app/client/job/[slug]/pay-beta/actions/processCardPayment.tsattik-backend/src/routes/guardianPayments.ts