41 "note": "CORRECTED 2026-08-17 (live-verified on poratus, matching an independent 2026-08-16 finding already recorded in plugins/pw-woocommerce-gift-cards.json): the plugin has no REST namespace of its own -- /wc-pimwick/v1/pw-gift-cards does not exist. Issuance data (code, amount, recipient) rides as line_item meta_data on /wc/v3/orders once the order reaches processing/completed. Redemption/balance data has no REST or order-meta surface at all -- see that same profile's gift-card-activity entity."
42 }
43 ],
44 "preferredWrite": {
45 "surface": "Gift Cards",
46 "endpoint": "POST /gift-cards/v1/gift-cards",
47 "writerId": null,
48 "verification": "verified-live",
49 "importSafe": true,
50 "bulk": false
51 },
52 "fallbacks": [
53 {
54 "classification": "cms",
55 "when": "Only for redemption/transaction history, which has no public write surface, or for source-only fields such as the original card design."
56 }
57 ],
58 "reliability": {
59 "status": "partially-reliable",
60 "flags": []
61 },
62 "pitfalls": [
63 {
64 "code": "suppress-recipient-email-on-import",
65 "severity": "blocker",
66 "summary": "Omit `giftCard.notificationInfo` entirely on import. Including it emails the recipient — immediately when `notificationDate` is empty — so a historical import would re-mail every card holder."
67 },
68 {
69 "code": "code-length-8-to-20",
70 "severity": "blocker",
71 "summary": "`giftCard.code` is constrained to 8-20 characters and is immutable. Source codes outside that range cannot be preserved, which breaks the code customers already hold; surface affected cards before import rather than silently rewriting them."
72 },
73 {
74 "code": "code-alphanumeric-only",
75 "severity": "blocker",
76 "summary": "VERIFIED LIVE 2026-08-17 (msid fd0ad9fb-d439-406f-8a00-b6c6ad0d95ab): `giftCard.code` also rejects any non-alphanumeric character outright — a hyphenated code returns `428 INVALID_CODE: \"Gift Card Code can only contain letters(A-z) and numbers(0-9)\"`, independent of the 8-20 length check above. This is a bigger deal than the length pitfall for sources whose default code format includes separators (e.g. PW WooCommerce Gift Cards' `XXXX-XXXX-XXXX-XXXX`) — EVERY such code is rejected as-is, not just outliers. Stripping non-alphanumeric characters produces an acceptable code (confirmed live: create + read-back succeeded), but that changes what the customer already holds — treat it the same as the length pitfall: report affected cards and get an explicit decision, don't strip silently by default."
77 },
78 {
79 "code": "full-code-returned-only-at-create",
80 "severity": "blocker",
81 "summary": "The create response is the only place the full unobfuscated code appears; Get/Query return it masked (`****-****-****-4444`) plus `codeSuffix`. Persist the code-to-id crosswalk in the same step as the create or it is unrecoverable."
82 },
83 {
84 "code": "balance-is-read-only",
85 "severity": "warning",
86 "summary": "Only `initialValue` is settable; `balance` is derived from transactions and there is no public adjust-balance method. A partly-spent card must either be created at its remaining value (loses face value and spend history) or created at face value and reduced via a redeem call (preserves both, adds a synthetic transaction). See spec 0042 Decision 5's ONE-policy-per-run rule (Policy A/B, never both for the same card) and lib/gift-card-balance-reconciliation.js's buildBalanceReconciliationReport() (fixed 2026-08-18) for the reporting-only drift check this implies — it never proposes a balance patch, only disable-and-replace or investigate, for exactly the no-adjust-balance-method reason stated here."
87 },
88 {
89 "code": "no-transaction-history-import",
90 "severity": "warning",
91 "summary": "No public method writes historical redemption transactions, so per-card spend history is not reproducible natively."
92 }
93 ],
94 "mappingGuidance": [
95 "Always send `source: \"MANUAL\"` — it is required, and it distinguishes imported cards from cards bought through a Wix order.",
96 "Pass a stable `idempotencyKey` derived from the source card id so a retried import cannot double-issue a card.",
97 "`expirationDate` is accepted at create, so original expiry dates are preserved.",
98 "Decide the partial-balance policy in the mapping plan and surface it in the execution plan; do not default silently.",
99 "Amounts are decimal strings with at most 2 decimal places, and currency is ISO-4217 alphabetic.",
100 "CORRECTED 2026-08-17 (VERIFIED LIVE): `currency` is a TOP-LEVEL field on the giftCard object, NOT nested inside `initialValue`. `{ giftCard: { initialValue: { amount, currency } } }` fails with `400 currency must not be empty`; the working shape is `{ giftCard: { initialValue: { amount }, currency, source, code, idempotencyKey } }`."
122 "path": "searched: gift card, stored value, voucher, store credit — across business-solutions/gift-cards and e-commerce/payments/gift-cards (2026-08-01)"
127 "note": "2026-08-17: installed the Wix Gift Cards app (appDefId d80111c5-a0f4-47a8-b63a-65b54d774a27, per the 'About Apps Created by Wix' table — the site had no gift-card app installed at all, which is what a first create attempt surfaces as `428 RISE_ACCOUNT_DOES_NOT_EXIST_FOR_META_SITE`, not a code bug), then successfully created and read back a real gift card built by rp-target-wix/lib/gift-card-build.js from a real PW WooCommerce Gift Cards order (source-side generated the same day — see plugins/pw-woocommerce-gift-cards.json). This is what surfaced both the currency-placement correction above and the code-alphanumeric-only pitfall. Not exercised: Send Gift Card Email, Query/Search, redeem-at-Wix-checkout, or disabling/deleting a card (the one test card was left live on the site, same as the tax-region precedent in research/poratus-first-live-run-findings.md finding #30)."