14 "objectPageException": "The Order Transactions API is not modelled as a fetchable object with its own '…-object' reference page. Its reference tree exposes an introduction, terminology, sample flows and the methods (Add Payments, List Transactions For Single/Multiple Orders, Update Payment Status), with no object page for Payment or OrderTransactions. Confirmed by a ranked SearchWixRESTDocumentation query for the order-transactions object reference on 2026-09-03, which returned the methods and articles cited in evidence[] and no object page. The field shape here is taken from the Add Payments method schema.",
15 "dependsOn": [
16 "ecom/order"
17 ],
18 "writtenInTaskOf": "ecom/order",
19 "sourceAliases": [
20 {
21 "sourceSystem": "woocommerce",
22 "sourceEntity": "order_payment_meta",
23 "routes": [
24 "/wc/v3/orders/{id}"
25 ],
26 "confidence": "high"
27 }
28 ],
29 "preferredWrite": {
30 "surface": "eCom Order Transactions",
31 "endpoint": "GET /ecom/v1/payments/orders/{orderId} then POST /ecom/v1/payments/orders/{orderId}/add-payment",
32 "writerId": "writeOrderPayment",
33 "verification": "verified-live",
34 "importSafe": true,
35 "bulk": false
36 },
37 "fallbacks": [],
38 "reliability": {
39 "status": "reliable",
40 "flags": []
41 },
42 "existenceGate": {
43 "summary": "A payment record is written only when the source supplies BOTH a positive amount and a paid date. An order total, a gateway-shaped key, an order status, and a transaction reference are each insufficient.",
44 "required": [
45 "a source amount that parses and is greater than zero",
46 "a source paid date"
47 ],
48 "neverSufficient": [
49 "the order total on its own",
50 "the order status on its own",
51 "a resolved provider transaction reference on its own"
52 ],
53 "outcomes": [
54 "payment-written",
55 "payment-reconciled-existing",
56 "payment-not-written-no-amount",
57 "payment-not-written-no-paid-date",
58 "payment-not-written-multi-payment",
59 "payment-not-written-currency-mismatch",
60 "payment-reconciliation-ambiguous",
61 "payment-target-read-failed",
62 "payment-failed"
63 ]
64 },
65 "fieldMapping": [
66 {
67 "target": "amount.amount",
68 "rule": "Required. Absent, unparseable or non-positive means no payment record."
69 },
70 {
71 "target": "createdDate",
72 "rule": "Required, from the source paid date. Never defaulted to the order date or migration day: omitting it makes the API stamp the current time."
73 },
74 {
75 "target": "status",
76 "rule": "Literal APPROVED, written only after the existence gate passes. PAYMENT-LEVEL field.",
91 "rule": "From verified method classification: true for known offline, false for known online, OMITTED and counted when unknown. Omission is not a synonym for false."
105 "rule": "NEVER SET. Writable, but no reusable credential can accompany it, so setting it asserts a charging agreement that does not exist."
106 },
107 {
108 "target": "refundDisabled",
109 "rule": "Not set, pending verification that it does not block an externalRefund write."
110 },
111 {
112 "target": "currency",
113 "rule": "No such field exists on a payment. Wix derives it from the order, so a source/target currency mismatch stops the payment write and is reported."
120 "summary": "An order total exists on orders that were never paid. VERIFIED LIVE on a production WooCommerce store: `_order_total` is present on cancelled and failed orders, which almost never carry a paid date. Creating an APPROVED payment wherever a total exists would mint approved payments for unpaid orders, overstating revenue in a more authoritative-looking place than the bug it replaced. Require a paid date."
121 },
122 {
123 "code": "status-allowlist-is-not-the-gate",
124 "severity": "blocker",
125 "summary": "Do NOT gate on an allowlist of 'paid' order statuses. VERIFIED LIVE: a store's terminal paid state can be `wc-processing` while `wc-completed` is nearly unused, so an allowlist keyed on 'completed' would import almost none of the paid orders. Status semantics are per-store. Status is a conflict/report axis only: a paid date on a cancelled order is preserved and the contradiction counted, and an unfamiliar status never blocks a real paid date."
126 },
127 {
128 "code": "status-is-a-payment-level-field",
129 "severity": "warning",
130 "summary": "SCHEMA DRIFT FOUND 2026-09-03 while re-checking the live Add Payments schema. `status` is a payment-level field, sibling to `amount` and `createdDate`, and that is the current field. The previous writer nested it inside `regularPaymentDetails`, which the schema still carries as a DEPRECATED field marked as replaced by the top-level one -- so the earlier claim that those requests \"asserted nothing about status\" was too absolute: they used the deprecated shape, whose behaviour is not guaranteed to persist. Write the top-level field. Pinned by tests/target-wix/order-payment-field-mapping-test.js."
135 "summary": "DOCS SAY one thing and the API does another. The schema states that `providerTransactionId` and `gatewayTransactionId` are \"not returned for offline payments\". LIVE-VERIFIED 2026-09-04 on a disposable site: an offline payment written WITH a `providerTransactionId` read it straight back. So the documented behaviour is wrong, or at least not universal. Reconciliation for offline payments matches on the fact fingerprint rather than the reference, which works either way -- it was built for the documented behaviour and is now simply belt-and-braces. Do NOT rely on the reference being absent, and do not treat its presence as drift. RE-CONFIRMED 2026-10-27 through the import client: an offline payment's reference read straight back again."
140 "summary": "SCHEMA-DOCUMENTED: the order's `paymentStatus` is recalculated asynchronously after Add Payments, so reading the order immediately afterward may still show the previous status. Verification must not assert on `order.paymentStatus` right after the write. OBSERVED 2026-10-27: the order read `PAID` both immediately after the write and 8 s later, so the lag was not seen there; the rule stands because one fast run does not prove it never lags."
145 "summary": "DOCS WRONG, LIVE-VERIFIED 2026-10-27 on a disposable site. The schema says that if any payment's external transaction id already exists on the order, the ENTIRE Add Payments call fails and no payments are added. Live, a two-payment call whose second payment repeated an existing `providerTransactionId` returned 200 and added BOTH, leaving two payments with the same reference. So the API gives NO duplicate protection: the read-and-reconcile before every create is the only guard, and a failed target read must still resolve to `payment-target-read-failed`, never to permission to create. A reference that matches twice is reported as `payment-reconciliation-ambiguous` and nothing is written."
150 "summary": "The replaced implementation selected a payment with `(payments || []).find((p) => !p.refundDisabled)?.id`. That is identity-free: it matches an unrelated record, including one the merchant created by hand in the Business Manager, and would attach a refund to the wrong payment. Match by provider transaction reference when one was defensibly sourced, otherwise by an exact fingerprint of amount, paid date, normalized method name, offline marker and card display fields. Exactly one match reconciles; more than one is ambiguous and prohibits creation."
151 },
152 {
153 "code": "paid-date-timezone-is-a-real-loss",
154 "severity": "warning",
155 "summary": "WooCommerce records the moment an order was paid TWICE: the `_gmt` sibling in universal time, and the plain field on the shop's wall clock with the zone stripped off. They are one payment described in two clocks, not two events, so the loss is per stamp and one shop offset cannot be correct for both. A paid stamp arrives in three shapes -- an epoch stamp, a full ISO instant, and a bare 'YYYY-MM-DD HH:MM:SS' wall-clock string with no zone; only the first two are unambiguous, and only the third is affected by any of this. Declare the clock each source key is written in: a key that is universal by the source schema's definition takes NO shop offset, so supplying the site's UTC offset leaves it exactly where it stands, and a key on the shop's wall clock takes the offset when one is known. A wall-clock stamp whose clock nobody has supplied is read as UTC and COUNTED as `paid-date-timezone-assumed` against that field, because silently shifting a payment by the shop's offset is a fidelity loss a merchant may discover later while reconciling a tax period; never invent the offset from the shop's address, and never express 'already universal' as an offset of zero, which asserts a measurement about the shop in place of a definition about the key."
160 "summary": "LIVE-VERIFIED 2026-09-04: an immediate List after a successful schema create reported the new fields ABSENT, while an order PATCH moments later accepted values for them -- which the API only allows when the schema contains them. The write had landed; the read was stale. Verifying once therefore produces a false 'not verified after write' on every fresh provision, which would block dependent writers. The re-read retries with backoff before believing a negative result."
161 },
162 {
163 "code": "live-round-trip-verified",
164 "severity": "info",
165 "summary": "LIVE-VERIFIED 2026-10-27 through the real import client (ecom/order import-task capability and readiness grant): amount, source createdDate, payment-level status APPROVED, method name, offline flag, providerTransactionId and card last-four/brand all read back as written, and the read-back fingerprint equals the written one. A rerun in the same process and in a fresh process reconciles (by reference, or by fingerprint for offline payments) and adds nothing; a lost Add Payments response is recovered by the rerun with no duplicate; an unrelated payment already on the order is never taken as ours. Limits hold: `providerTransactionId` 100 and payment method name 150 characters are accepted, 101 and 151 are rejected with 400."
166 }
167 ],
168 "mappingGuidance": [
169 "Run the existence gate before anything else. Ineligible orders must never reach the write path, which also keeps target reads proportional to evidenced payments rather than to the whole order population.",
170 "Resolve the transaction reference, card display fields and offline classification through the source-side registry, not from a payment-method slug: the slug names the checkout plugin, not necessarily the processor behind it.",
171 "One payment record per order in phase 1. A source exposing more than one distinct payment amount is reported as `payment-not-written-multi-payment`, never summed into one record.",
172 "Report the payment outcome for every source order. The mutually exclusive outcomes must sum to the discovered source-order population on every run, so a clean re-run reports reconciled records rather than disguising them as skips."
179 "note": "Full method schema re-checked 2026-09-03. Established: status is payment-level; createdDate is settable and defaults to now when omitted; providerTransactionId max 100; userDefinedName.custom max 150; lastFourDigits max 4; brand max 100; 50 payments per call; 100 payments per order; duplicate external transaction id fails the entire call; order paymentStatus recalculates asynchronously."
194 "note": "Introduction, read 2026-09-03. Confirms two behaviours this entity depends on, in Wix's own words: 'The Order Transactions API records payments and refunds. It doesn't charge or refund funds. Adding a payment record or updating a payment status has no effect on the actual money movement.' And: 'After a payment record is added or updated, the order's paymentStatus is recalculated asynchronously. Reading the order immediately afterward may still show the previous payment status.'"
195 },
196 {
197 "type": "live-run",
198 "path": "anonymized-live-verification",
199 "note": "Live probe 2026-10-27 on a disposable site: round-trip, reruns, lost-response recovery, duplicate-reference refutation, length limits."