"Payload drop" scan experience — hand over a link or code on scan
CI / policy (push) Has been cancelled
CI / backend (push) Has been cancelled
CI / profile (push) Has been cancelled
CI / mobile (push) Has been cancelled

Experience #8: a published card can carry a payload that's shown prominently on
scan — a primary link CTA, or a discount/access code with tap-to-copy.

- Backend: demo publish accepts an optional payload {kind, label, value},
  stored in definition.profile.payload (flows through the public profile). Test
  extended to assert it. Gate green (120 tests).
- Web ([handle].astro): renders a "link" payload as a big CTA button and a
  "code" payload as a dashed chip with copy-on-tap. astro check clean.
- Mobile (publish.tsx): payload kind chips (None/Link/Code) + label + value,
  sent with publishToWeb.

Verified live: publish with a code payload -> profile shows a copyable CARD20
chip; link payload -> CTA button.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This commit is contained in:
Omar Sobh
2026-06-05 20:49:55 -05:00
co-authored by Claude Opus 4.8
parent 895ca4993a
commit a61d0221d1
6 changed files with 149 additions and 3 deletions
@@ -344,7 +344,8 @@ async fn demo_publish_creates_scannable_profile_with_welcome() {
"title": "Founder",
"links": [{ "label": "Site", "url": "https://cardclaws.com" }],
"welcomePrompt": "a calm forest at dawn",
"welcomeMessage": "Great to meet you"
"welcomeMessage": "Great to meet you",
"payload": { "kind": "code", "label": "20% off", "value": "CARD20" }
})),
)
.await;
@@ -352,13 +353,17 @@ async fn demo_publish_creates_scannable_profile_with_welcome() {
let handle = body["handle"].as_str().unwrap();
assert!(body["profileUrl"].as_str().unwrap().ends_with(handle));
// The published card resolves publicly with the owner name + welcome.
// The published card resolves publicly with the owner name + welcome + payload.
let (status, profile) = app
.request("GET", &format!("/v1/cards/handle/{handle}"), None, None)
.await;
assert_eq!(status, StatusCode::OK);
assert_eq!(profile["ownerDisplayName"], "Omar Sobh");
assert_eq!(profile["welcome"]["message"], "Great to meet you");
assert_eq!(
profile["definition"]["profile"]["payload"]["value"],
"CARD20"
);
}
#[tokio::test]