feat(cards): add PIN management endpoints - #897
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. 2 Skipped Deployments
|
|
Preview deployment for your docs. Learn more about Mintlify Previews.
|
✱ Stainless preview builds for gridThis PR will update the cli go kotlin openapi php python ruby typescript Edit this comment to update them. They will appear in their respective SDK's changelogs. ✅ grid-typescript studio · code · diff
✅ grid-openapi studio · code · diff
✅ grid-ruby studio · code · diff
✅ grid-kotlin studio · code · diff
✅ grid-go studio · code · diff
✅ grid-python studio · code · diff
✅ grid-php studio · code · diff
✅ grid-cli studio · code · diff
This comment is auto-generated by GitHub Actions and is automatically kept up to date as you push. |
Adds three card PIN endpoints and a pinStatus field on the Card resource.
- POST /cards/{id}/pin takes a PIN block the client encrypted for the card
processor. Grid forwards it verbatim and never sees the PIN.
- POST /cards/{id}/pin/session mints a short-lived session token for the
processor's hosted PIN-entry iframe, for platforms without their own PIN
UI. The processor returns a token rather than a URL, so the response
carries the token and the client's embed SDK builds the iframe from it.
- POST /cards/{id}/pin/unblock clears a PIN blocked by three consecutive
incorrect entries, without changing it.
- Card.pinStatus reports NOT_SET / OK / BLOCKED, and is absent on cards whose
processor does not offer PIN management.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GsstRe1f8aoPXm1kYQw2Vm
2c2098d to
8a85f10
Compare

Reason
Cardholders have no way to set a PIN on a Grid-issued card. Without one, PIN-verified transactions decline, and a cardholder who blocks their PIN through incorrect attempts has no recovery path through Grid at all.
Overview
Adds three endpoints and one field. The two set paths are alternatives — a platform picks whichever matches how much of the PIN-entry experience it wants to own — and neither lets a PIN reach Grid.
POST /cards/{id}/pinPOST /cards/{id}/pin/sessionPOST /cards/{id}/pin/unblockCard.pinStatus(NOT_SET/OK/BLOCKED) makes the state observable, and is absent on cards whose processor offers no PIN management.Notes for review:
POST /cards/{id}/reveal: the processor's embed-session endpoint answers with an opaque session credential and no URL, and the iframesrcits SDK builds is not publicly documented. Returning apinEmbedUrlwould have meant inventing one, so the response carriessessionTokenand the client's embed SDK builds the iframe from it.encryptedPinBlockis deliberately opaque. Grid cannot validate it, so the docs put the burden of correct encryption on the client and say plainly that a rejected block returns400 INVALID_INPUTwith no finer diagnosis.CardPinSessionResponsepoints readers atpinStatusto confirm the outcome rather than implying the mint itself means success.PATCH /cards/{id}with a status flag:CardUpdateRequestalready requires one ofstate/fundingSources/maxSpend*and carriesCLOSEDexclusivity rules, and threading a one-way status through that is more churn than a 204.Everything else follows the reveal endpoint — same security scheme, same audit-logging language, same short-lived-bearer-secret warnings.
Test Plan
make build— bundle regenerates cleanly.npx @redocly/cli lint openapi.yaml— valid, and the warning count is unchanged frommain(55 before, 55 after), so these files add none.npx @stoplight/spectral-cli lint openapi.yaml --fail-severity=error— 0 errors, and no finding references any new schema or path (the info count drops by one against baseline, sinceCardPinStatuscarries the exampleCardStatelacks).The sparkcore implementation and the ops UI are stacked in webdev: lightsparkdev/webdev#34390 → #34391 → #34392 → #34393 → #34394.
🤖 Generated with Claude Code
https://claude.ai/code/session_01GsstRe1f8aoPXm1kYQw2Vm